Java泛型学习(一)通配符学习 —— Java Generic's Wildcards

Java Generic's wildcards is a mechanism in Java Generics aimed at making it possible to cast a collection of a certain class, e.g A, to a collection of a subclass or superclass of A. This text explains how.

理解:Java的泛型通配符机制旨在实现集合的类型转换。例如集合A,转换为A的子类集合或A的父类集合。

Here is a list of the topics covered:

    The Basic Generic Collection Assignment Problem

    Imagine you have the following class hierarchy:

    public class A { }
    public class B extends A { }
    public class C extends A { }

    The classes B and C both inherit from A.

    Then look at these two List variables:

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

    Can you set listA to point to listB ? or set listB to point to listA? In other words, are these assignments valid:

    listA = listB;
    
    listB = listA;

    The answer is no in both cases. Here is why:

    In listA you can insert objects that are either instances of A, or subclasses of A (B and C). If you could do this:

    List<B> listB = listA;

    then you could risk that listA contains non-B objects. When you then try to take objects out of listB you could risk to get non-B objects out (e.g. an A or a C). That breaks the contract of the listB variable declaration.

    理解:listA中可以包含A的实例,也可以包含A的子类的实例,比如B和C。当你将listB的引用指向了listA的实例,那么就存在危险,因为调用listB的get方法时你可能取出实例A或者实例C,这违反了listB声明的变量类型限制(只能取出实例B或者B的子类的实例)。

    Assigning listB to listA also poses a problem. This assignment, more specifically:

    listA = listB;

    If you could make this assignment, it would be possible to insert A and C instances into the List<B> pointed to by listB. You could do that via the listA reference, which is declared to be of List<A>. Thus you could insert non-B objects into a list declared to hold B (or B subclass) instances.

    理解:当你将listA的引用指向了listB的实例的时候,也存在危险,因为你可以在listB的实例中加入A或者C的对象,而且是你只能加入B的实例或者B的子类的实例。

    When are Such Assignments Needed?

    The need for making assignments of the type shown earlier in this text arises when creating reusable methods that operate on collections of a specific type.

    Imagine you have a method that processes the elements of a List, e.g. print out all elements in the List. Here is how such a method could look:

    public void processElements(List<A> elements){
       for(A o : elements){
          System.out.println(o.getValue());
       }
    }

    This method iterates a list of A instances, and calls the getValue() method (imagine that the class A has a method called getValue()).

    As we have already seen earlier in this text, you can not call this method with a List<B> or a List<C> typed variable as parameter.

    理解:泛型通配符所要解决的问题就是集合函数的可重用性。

    Generic Wildcards

    The generic wildcard operator is a solution to the problem explained above. The generic wildcards target two primary needs:

    • Reading from a generic collection
    • Inserting into a generic collection

    There are three ways to define a collection (variable) using generic wildcards. These are:

    List<?>           listUknown = new ArrayList<A>();
    List<? extends A> listUknown = new ArrayList<A>();
    List<? super   A> listUknown = new ArrayList<A>();

    The following sections explain what these wildcards mean.

    理解:泛型通配符需要满足两个需求:读和写。

    The Unknown Wildcard

    List<?> means a list typed to an unknown type. This could be a List<A>, a List<B>, a List<String> etc.

    Since the you do not know what type the List is typed to, you can only read from the collection, and you can only treat the objects read as being Object instances. Here is an example:

    public void processElements(List<?> elements){
       for(Object o : elements){
          System.out.println(o);
       }
    }

    The processElements() method can now be called with any generic List as parameter. For instance a List<A>, a List<B>List<C>, a List<String> etc. Here is a valid example:

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

     理解:List<?>未知类型通配符,可以是List<A>,List<B>和List<C>。但只能用于集合的读取,而且只能以Object的形式进行读取。

    The extends Wildcard Boundary

    List<? extends A> means a List of objects that are instances of the class A, or subclasses of A (e.g. B and C).

    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. Here is an example:

    public void processElements(List<? extends A> elements){
       for(A a : elements){
          System.out.println(a.getValue());
       }
    }

    You can now call the processElements() method with either a List<A>List<B> or List<C>. Hence, all of these examples are valid:

    List<A> listA = new ArrayList<A>();
    processElements(listA);
    
    List<B> listB = new ArrayList<B>();
    processElements(listB);
    
    List<C> listC = new ArrayList<C>();
    processElements(listC);

    The processElements() method still cannot insert elements into the list, because you don't know if the list passed as parameter is typed to the class AB or C.

    理解:继承通配符。List<? extends A>表示集合中的元素可以是A的实例或者A的子类的实例。你可以从集合中读取元素,他们会强制转换为A的实例,但是添加元素仍然是不安全的,因为你不知道传进的参数是A,B,还是C。

    The super Wildcard Boundary

    List<? super A> means that the list is typed to either the A class, or a superclass of A.

    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. Here is an example:

    public static void insertElements(List<? super A> list){
        list.add(new A());
        list.add(new B());
        list.add(new C());
    }

    All of the elements inserted here are either A instances, or instances of A's superclass. Since both B and C extend A, if A had a superclass, B and C would also be instances of that superclass.

    You can now call insertElements() with either a List<A>, or a List typed to a superclass of A. Thus, this example is now valid:

    List<A>      listA      = new ArrayList<A>();
    insertElements(listA);
    
    List<Object> listObject = new ArrayList<Object>();
    insertElements(listObject);

    The insertElements() method cannot read from the list though, except if it casts the read objects to Object. The elements already present in the list when insertElements() is called 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. However, since any class eventually subclass Object you can read objects from the list if you cast them to Object. Thus, this is valid:

    Object object = list.get(0);

    But this is not valid:

    A object = list.get(0);

    理解:List<? super A>表示集合中元素的类型可以是A或者A的父类。当你知道集合中元素的类型是A的实例或者A的父类的实例的时候,插入A和A的子类的实例变得可行(子类B和C的实例是A的超类的实例)。读取集合中的元素的时候,只能用Object对象进行读取,因为你不知道加入集合中元素的类型,但是他们都可以强制转换为Object类的元素。

    posted on 2015-12-27 09:09  gyt929458988  阅读(733)  评论(0编辑  收藏  举报