Java泛型(通配符)

Java Generics (Wildcards)

关于Java中的通用通配符,我有两个问题:

  • ListList有什么区别?

  • 什么是有界通配符,什么是无界通配符?


  • 在第一个问题中,是有界通配符的例子。一个无边界通配符看起来像,基本上意味着。它松散地表示泛型可以是任何类型。有界通配符(通过表示必须扩展特定类型(称为上界)或必须是特定类型的祖先(称为下界)来限制类型。

    Java教程对文章通配符中的泛型有很好的解释,并且通配符也更有趣。


    如果您有一个类层次结构a,b是a的子类,c和d都是b的子类,如下所示

    1
    2
    3
    4
    class A {}
    class B extends A {}
    class C extends B {}
    class D extends B {}

    然后

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    List<? extends A> la;
    la = new ArrayList();
    la = new ArrayList<C>();
    la = new ArrayList<D>();

    List<? super B> lb;
    lb = new ArrayList<A>(); //fine
    lb = new ArrayList<C>(); //will not compile

    public void someMethod(List<? extends B> lb) {
        B b = lb.get(0); // is fine
        lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
    }

    public void otherMethod(List<? super B> lb) {
        B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
        lb.add(new B()); // is fine, as we know that it will be a super type of A
    }

    有界通配符类似于? extends B,其中b是某种类型。也就是说,类型未知,但可以在其上放置"bound"。在这种情况下,它是由某个类所限定的,该类是B的一个子类。


    Josh Bloch还很好地解释了在这个Google IO视频谈话中何时使用superextends,他提到了制片人extends消费者super助记键。

    在演示幻灯片中:

    Suppose you want to add bulk methods to Stack

    void pushAll(Collection src);

    – src is an E producer

    void popAll(Collection dst);

    – dst is an E consumer


    有时您可能希望限制允许传递给类型参数的类型的类型。例如,对数字进行操作的方法可能只希望接受数字或其子类的实例。这就是有界类型参数的用途。

    1
    Collection<? extends MyObject>

    意味着它可以接受所有与MyObject有关系的对象(即任何属于MyObject类型的对象,或者我们可以说任何MyObject子类的对象)或MyObject类的对象。

    例如:

    1
    2
    3
    4
    5
    class MyObject {}

    class YourObject extends MyObject{}

    class OurObject extends MyObject{}

    然后,

    1
    Collection<? extends MyObject> myObject;

    只接受myObject或myObject的子对象(即任何类型为ourObject或yourObject或myObject的对象,但不接受myObject的超类对象)。


    一般来说,

    If a structure contains elements with a type of the form ? extends E, we can get elements out of the structure, but we cannot put
    elements into the structure

    1
    2
    3
    4
    5
    6
    List<Integer> ints = new ArrayList<Integer>();
    ints.add(1);
    ints.add(2);
    List<? extends Number> nums = ints;
    nums.add(3.14); // compile-time error
    assert ints.toString().equals("[1, 2, 3.14]");

    要将元素放入结构中,我们需要另一种称为Wildcards with super的通配符,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     List<Object> objs = Arrays.<Object>asList(2, 3.14,"four");
        List<Integer> ints = Arrays.asList(5, 6);
        Collections.copy(objs, ints);
        assert objs.toString().equals("[5, 6, four]");

        public static <T> void copy(List<? super T> dst, List<? extends T> src) {
              for (int i = 0; i < src.size(); i++) {
                    dst.set(i, src.get(i));
             }
        }


    预先要求

    1
    2
    3
    4
    5
    6
    public class A { }
    public class B extends A { }
    public class C extends A { }

    List<A> listA = new ArrayList<A>();
    List listB = new ArrayList();

    问题

    1
    2
    listB = listA; //not compiled
    listA = listB; //not compiled

    listB = listA;在lista中,您可以插入属于a的实例或a(b和c)的子类的对象。然后,您可能会冒这样的风险:listA包含非B对象。当你试图从listB中取出物体时,你可能会冒险取出非B物体(例如A或A C)。这违反了listB变量声明的合同。

    listA = listB;如果您可以执行此任务,则可以将a和c实例插入到listB所指向的列表中。您可以通过listA参考文件来完成这项工作,该参考文件被声明为清单。因此,可以将非B对象插入到声明为保存B(或B子类)实例的列表中。

    目的

    When creating reusable methods that operate on collections of a
    specific type.

    如果要从列表中读取.get(),则应使用List (上限)。

    When you know that the instances in the collection are of instances of A or subclasses of A, it is safe to read the instances of the collection and cast them to A instances.

    You can not insert elements into the list, because you don't know if the list is typed to the class A, B or C.

    如果要在列表中插入.add(),则应使用List (下限)。

    When you know that the list is typed to either A, or a superclass of A, it is safe to insert instances of A or subclasses of A (e.g. B or C) into the list.

    You cannot read from the list though, except if it casts the read objects to Object. The elements already present in the list could be of any type that is either an A or superclass of A, but it is not possible to know exactly which class it is.

    请阅读更多内容-http://tutorials.jenkov.com/java-generics/widcards.html


    创建通用通配符是为了使对集合操作的方法更可重用。

    例如,如果一个方法有一个参数List,我们只能给List这个方法。在某些情况下,这种方法的功能是浪费的:

  • 如果这个方法只从List中读取对象,那么应该允许我们将List赋予这个方法。(因为a-sub是a)
  • 如果这个方法只向List插入对象,那么应该允许我们将List给予这个方法。(因为a是a-super)