Java泛型边界问题,super关键字

Posted linlei2099

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java泛型边界问题,super关键字相关的知识,希望对你有一定的参考价值。

背景

不要对super望文生义

super关键字,用于类的方法中表示指向父类对象的引用。
在泛型边界语法中指出泛型下界。

假设有继承关系,A <- B <- C <- D <- E

   void f( List<? super C>  param){...}

List<? super C>List<B>,List<A>,List<C>,List<Object>或者List<? super B>,List<? super A>的父类,对于接受List<? super C>为参数的方法f中,传递上述类型时,可以隐式向上转型,安全。在方法中,取出的item都是Object类型,List<? super C>取出的类型都是Object,不是C或者其他的具体类

如果需要cast取出的item为具体类型,程序员自己保证存在List中的对象都是可以安全强制转型的。最好的应用场景是不需要针对具体类型的item进行处理,其次是程序员自己保证所有item可以安全强制转型。

方法f无法接受List<D>List<E>,cast强制转型也无法通过编译,Java不接受没有层次关系的两个类型的强制转型。这是泛型规则规定的:它们和List<B>,List<A>,List<C>没有父子关系。

假设方法内

     List<? super C>  param;
     param.add(new B());// ERROR
     param.add(new Object());//ERROR
     param.add(new D());//right

List<? super C>存入item的限制很大而且看起来有点奇怪:任何C的父类和C类型的对象都不能存入,C的子类可以存入。看起来和前一节所规定的List<...>父子关系相矛盾。

先说结论:super限制的泛型不适合存,只适合消费它

    PECS(producer extends,Consumer super)规则。
  • 为什么List<? super C> param取出来的是Object,而不是具体类型?

因为在声明param的时候只要求这个param是List<? super C>,翻译成人话就是声明param是List<B>,List<A>,List<C>,List<Object>或者是List<? super B>,List<? super A>中的一种,不能保证是具体的哪一种,所以编译器不知道取出的item是哪一种具体类型。如果程序员自己能够保证传入的是同一种具体类型的item,那么自行强制转型,但是编译器只能简单地约定从List<? super C>中取出的Object类型。

  • 为什么不能向List<? super C>存入A B Object类型,而可以存入C和D E?

因为存入A B或者Object类型可能会破坏原来List的类型一致性,正是为了避免这个问题才在JDK5引入泛型特性的,因此不能允许。而存入C的子类时,通过隐式转换为C可以保证不因此破坏List中item类型的一致。

  • 既然不能保证取出的item是同一种具体类型,要super有何用?

super没有限制死f接受的是哪一种具体的List<..>,同时也做出了一定的限制,不能是List<B>,List<A>,List<C>,List<Object>或者List<? super B>,List<? super A>之外的List。如果传递一个没有边界的List<?>强制转型那就是另外的事情了。

场景(由于某些原因,子类DE的item list不需要被f处理):

    void f( List<? super C>  param){
        if(param.get(0) instanceof A){
            for(Object item : param){
                A a = (A)item;
                a.doAsA();
            }
        } else(param.get(0) instanceof B){
             for(Object item : param){
                B b = (B)item;
                b.doAsB();
            }
        }
    }
    

上面的场景代码还是有点牵强,没有想到实际的应用场景。这可能也是关键字super限制泛型下界这个java特性的不足吧,但是在extends之外多提供一种选择,也不算坏事。

extends

PECS规则指出extends用于生产item到泛型的时候。

    List<? extends C>  void f(){
        List<? extends C> ret = getNewList();
        for(int i =0; i<10; i++){
            ret.add(new C());
            ret.add(new D());
        }
        return ret;
    }

调用f的代码可以获得一个List,编译器确保该List中的item都是C类型。比较好理解,看起来extends更强大,有没有使用super比使用extends来限定泛型边界的更好的场景?

大概就是当上界不需要限制的时候,这时如果使用<? extends Object>,那么就相当于没有限制。
所以super不是完全可以被extends替代的,多一种选择更好。



以上是关于Java泛型边界问题,super关键字的主要内容,如果未能解决你的问题,请参考以下文章

通配符和边界问题

深入Java泛型(三泛型的上下边界)

深入Java泛型(三泛型的上下边界)

Java泛型中extends和super的区别

Android开发之深入理解泛型extends和super的区别

Android开发之深入理解泛型extends和super的区别