泛型方法中的多态

作者: 赵阳_c149 | 来源:发表于2019-08-06 17:36 被阅读2次

    读Core java Volume I eleventh version Chapter 8 Generic Programming 8.5.3 Translating Generic Methods一节的时候,有一段论述初看并不是十分理解,原因是一时忘记了overload和override的区别,想来真是惭愧。特地记录下来,供大家一笑。

    问题是这样的:

    假设有一个泛型类,

    public class Pair<T>{   
      private T first;   
      private T second;   
      public Pair() { 
        first = null; 
        second = null; 
      }   
      public Pair(T first, T second) { 
        this.first = first; 
        this.second = second; 
      }   
      public T getFirst() { return first; }   
      public T getSecond() { return second; }   
      public void setFirst(T newValue) { first = newValue; }   
      public void setSecond(T newValue) { second = newValue; }
    }
    

    定义他的一个子类:

    class DateInterval extends Pair<LocalDate>{   
      public void setFirst(LocalDate second)   {      
        if (second.compareTo(getFirst()) >= 0)         
          super.setSecond(second);   
      }   . . .
    }
    

    这里开发者的本意是override父类中的方法。但是在擦除之后,会遇到一个问题。具体说来,首先这个类被擦除为:

    class DateInterval extends Pair // after erasure
    {   
      public void setSecond(LocalDate second) { . . . }   
      . . .
    }
    

    此时,还存在着另外一个继承自Pair的setSecond方法,:

    public void setSecond(Object second)
    

    这是一个完全不同的方法,因为它有一个参数的类型是object而不是LocalDate。开发者的本意是利用java的多态机制以保证合适的方法在运行时被调用。但是很明显,这样做是有问题的。考虑以下代码:

    var interval = new DateInterval(. . .);
    Pair<LocalDate> pair = interval; // OK--assignment to superclass
    pair.setSecond(aDate);
    

    pair索引指向一个DateInterval的对象,当我们调用他的时候,是期望调用DateInterval.setSecond,然而,由于类型被擦除,多态受到了干扰,因此实际调用的是Pair.setSecond(Object)。为此,编译器生成了一个桥接(bridge)方法:

    public void setSecond(Object second) { setSecond((LocalDate) second); }
    

    我的理解:overload和override

    个人认为这段交待的不是很清楚。文中只是强调了多态的一种形式override,而没有提及多态的另外一种形式overload。如果用overload来解释这个问题,我觉得会跟好理解一些。

    开发者的本意是利用override机制实现多态,也就是说用

    public void setSecond(LocalDate second) 
    

    override

    public void setSecond(T newValue) 
    

    但是,由于编译阶段的擦除机制,override变成了overload,也就是说两个方法是并存的

    public void setSecond(LocalDate second) 
    public void setSecond(Object newValue) 
    

    因此在编译阶段,以下代码并没有按照开发者的预期去绑定子类的方法到对象上。

    对此,可以简单的做一个实验:

    public class TestGenericMethod {    
      public static void main(String...strings){
        Parent c = new Child();        
        c.setSecond(“”);    
      }
    }
    class Parent{    
      public void setSecond(Object second){        
        System.out.println("setSecond in parent");    
      }
    }
    class Child extends Parent{    
      public void setSecond(String second){        
        System.out.println("setSecond in Child");    
      }
    }
    

    运行代码,得到的输出为:

    setSecond in parent
    

    对此,该书的5.1.6节有详细的说明:

    Next, the compiler determines the types of the arguments supplied in the method call. If among all the methods called f there is a unique method whose parameter types are a best match for the supplied arguments, that method is chosen to be called. This process is called overloading resolution …

    “编译器决定了在方法调用时提供的变量类型。如果,在所有相同名字的方法中存在着一个唯一的方法,它的参数类型和提供的变量符合度最高,那么这个方法将被选择执行。这一过程称为overload…”

    所以,这里的根本问题是由于type参数的擦除,预期的override变成了overload。

    相关文章

      网友评论

        本文标题:泛型方法中的多态

        本文链接:https://www.haomeiwen.com/subject/opvwdctx.html