• Java 泛型 协变式覆盖和泛型重载


     Java 泛型 协变式覆盖和泛型重载

     @author ixenos

     

     

     

    1.协变式覆盖(Override)

    JDK 1.4及以前,子类方法如果要覆盖超类的某个方法,必须具有完全相同的方法签名,包括返回值也必须完全一样。

    JDK 5开始,只要子类方法与超类方法具有相同的方法签名,或者子类方法的返回值是超类方法的子类型(增加了对协变返回值的支持),就可以覆盖。这样有什么好处呢?以Object类的clone方法为例:

    class Object {
     ...
     public Object clone() { ... }
    }

    5.0以前,如果子类需要重载clone方法,必须像下面这样写代码:

    class Point {
     public int x;
     public int y;
     public Point(int x, int y) { this.x=x; this.y=y; }
     public Object clone() { return new Point(x,y); }
    }

    虽然在我们的Point类里,clone方法总是返回一个Point类型的对象,但却必须把返回类型写成Object,在外部使用clone方法时也必须使用恼人的强制类型转换。

    Java5.0以后,我们就可以利用新的覆盖规则,像下面这样编写代码:

    class Point {
     public int x;
     public int y;
     public Point(int x, int y) { this.x=x; this.y=y; }
     public Point clone() { return new Point(x,y); }
    }

     这样,我们就可以直接使用Point p2 = p1.clone(); 而不用强制类型转换了。


    2.泛型重载(overload)

           Java方法重载一般指在同一个类中的两个同名方法,规则是:两个方法必须具有不同的方法签名。因此形式参数必须不相同,使得编译器能够区分开这两个重载的方法。由于编译器不能仅仅通过方法的返回值类型来区分重载方法,所以如果两个方法只有返回类型不同,其它完全一样,编译是不能通过的。(泛型、重载是java语言级别的,但擦除技术是关于实现的,它关系到合法class文件的生成,而合法的class文件才能被jvm接受,jvm本来就支持签名相同,但返回类型不同的方法存在

     

      在java语言角度的添加这种限制也是自然的。比如两个方法:

     

      void test(int i);

     

      int test(int i);

     

      编译器不能确定到底应该调用哪个方法,所以这种情况在java中不允许存在。

     

      但是,对于这两个方法test(ArrayList<String> list)和test(ArrayList<Integer> list),在java语言的级别,即编译时,也可以是合法的重载!

      因为编译器可以通过参数类型信息来确定调用哪个版本。再加上返回类型不同,经过编译和类型擦除得到的两个方法是可以在class文件中共存的。这样问题就解决了。

     

    泛型方法的重载时,这个规则变化如下:

    class Overloaded {
     public static int sum(List<Integer> ints) {
        int sum = 0;
        for (int i : ints) sum += i;
        return sum;
     }
     public static String sum(List<String> strings) {
        StringBuffer sum = new StringBuffer();
        for (String s : strings) sum.append(s);
        return sum.toString();
     }
    }

    上面是两个泛型方法的重载例子,由于Java的泛型采用擦除法实现,List<Integer>List<String>在运行时是完全一样的,都是List类型。也就是,擦除后的方法签名如下:

    int sum(List)
    String sum(List)

    JVM允许这两个方法进行重载(overload!),虽然它们的方法签名相同(形参),只有返回值类型不同。这在两个普通方法的重载中是不允许的。

    当然了,如果两个泛型方法的参数在擦除后相同,而且返回值类型也完全一样,那编译肯定是不能通过的。

    类似地,一个类不能同时实现两个具有相同擦除的接口。如Class A implements Comparable<Integer>, Comparable<Long>

        

      总结一下,

      如果两个泛型方法在擦除泛型信息后,如果只是具有相同的参数类型,而返回值不一样,就可以进行重载;


     

    2016-09-05 17:39:18更新:

      此类泛型重载在JDK 1.7及以上编译时已不允许。

    JDK7、8是不可以编译的,需要用JDK6才可以(答案中的均使用oracle jdk提供的编译器)。

    首先,按道理这个本来就应该报错,从Java语言层面来说,方法重载依赖于相同的方法名、不同的参数个数、类型、顺序,而List<Integer>和List<String>类型擦除后都为List<E>,从而不符合方法重载的要求。
    但是,为什么会说这种依赖返回值可以通过甚至正常运行,原因在于,编译后的俩个方法在class中的signature分别为
    (Ljava/util/List<Ljava/lang/Integer;>;)I
    
    (Ljava/util/List<Ljava/lang/String;>;)Ljava/lang/String;
    
    它们可以合法的共存在一个class文件中。

    从jdk7开始呢,编译期做了check,保证了behavior一致,所以报错

    参考链接:


    作者:葫芦娃
    链接:https://www.zhihu.com/question/37802781/answer/75883080
    来源:知乎
    著作权归作者所有,转载请联系作者获得授权。

     

     

     

     

     

  • 相关阅读:
    Python进程池
    Python进程间通信
    python编程中的if __name__ == 'main与windows中使用多进程
    Python进程-实现
    python进程join()函数理解
    python 进程池Pool
    python多进程打印字符,加锁(Lock加锁)
    python进程、多进程
    正则表达式统计字符串中数字的个数
    python 处理xml
  • 原文地址:https://www.cnblogs.com/ixenos/p/5645741.html
Copyright © 2020-2023  润新知