繁体   English   中英

Java Generics(通配符)

[英]Java Generics (Wildcards)

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

  1. List<? extends T> List<? extends T>List<? super T> List<? super T>

  2. 什么是有界通配符,什么是无界通配符?

在你的第一个问题中, <? extends T> <? extends T><? super T> <? super T>是有界通配符的例子。 无界通配符看起来像<?> ,基本上意味着<? extends Object> <? extends Object> 它松散地意味着泛型可以是任何类型。 有界通配符( <? extends T><? super T> )通过说它必须扩展特定类型( <? extends T>被称为上限)或必须是特定类型的祖先( <? super T>被称为下界)。

Java 教程在通配符通配符更有趣的文章中对泛型有一些很好的解释。

如果您有一个类层次结构 A,B 是 A 的子类,而 C 和 D 都是 B 的子类,如下所示

class A {}
class B extends A {}
class C extends B {}
class D extends B {}

然后

List<? extends A> la;
la = new ArrayList<B>();
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 ? extends B其中 B 是某种类型。 也就是说,类型是未知的,但可以在其上放置“绑定”。 在这种情况下,它受某个类的限制,该类是 B 的子类。

Josh Bloch 在这个google io 视频演讲中也很好地解释了何时使用superextends ,他提到了Producer extends Consumer super助记符。

从演示幻灯片:

假设您想向Stack<E>添加批量方法

void pushAll(Collection<? extends E> src);

– src 是 E 生产者

void popAll(Collection<? super E> dst);

– dst 是 E 消费者

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

Collection<? extends MyObject> 

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

例如:

class MyObject {}

class YourObject extends MyObject{}

class OurObject extends MyObject{}

然后,

Collection<? extends MyObject> myObject; 

将只接受 MyObject 或 MyObject 的子对象(即类型为 OurObject 或 YourObject 或 MyObject 的任何对象,但不接受 MyObject 超类的任何对象)。

一般来说,

如果结构包含具有以下形式的元素? extends E ? extends E ,我们可以从结构中取出元素,但不能将元素放入结构中

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

 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));
         }
    }

创建通用通配符是为了使对 Collection 进行操作的方法更具可重用性。

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

  1. 如果此方法仅从List<A>读取对象,那么我们应该允许将List<A-sub>赋予此方法。 (因为 A-sub 是 A)
  2. 如果此方法只向List<A>插入对象,那么我们应该允许将List<A-super>赋予此方法。 (因为A是A超)

前提条件

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

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

问题

listB = listA; //not compiled
listA = listB; //not compiled

listB = listA; 在listA中,您可以插入作为A实例或A子类(B和C)的对象。 当使listB = listA您可能会listA包含非B对象的风险。 当你再尝试获得的物品运到listB你可以冒险让非B对象了(例如A或C)。 这破坏了listB变量声明的约定。

listA = listB; 如果可以进行此分配,则可以 A和C实例插入listB指向的List中。 您可以通过声明为List的listA引用来实现。 因此,您可以非B对象插入声明为包含B(或B子类)实例的列表中。

在此处输入图片说明

问题

如您所见,问题出在分配上 例如,您无法以这种方式创建一种方法来处理作为亲属的List的元素。

解决方案

通用通配符

您应该使用List <? extends A> 如果要从列表中读取.get() ,则List <? extends A> aka 上限 aka 协方差 aka 生产者

当您知道集合中的实例是A的实例或A的子类时,可以安全地读取集合的实例并将其强制转换为A实例。

无法将元素插入列表,因为您不知道列表是否键入到A,B或C类。

您应该使用List <? super A> List <? super A>又名下限又名逆变又名消费者 ,如果你要插入。新增()进榜

当你知道这个列表是类型化到任何一个,或A的超类,它是安全的插入 A(如B或C)的实例或子类到列表中。

但是,您不能从列表中读取内容 ,除非它将读取的对象强制转换为对象。 列表中已经存在的元素可以是A或A的超类的任何类型,但无法确切知道它是哪个类。

在此处输入图片说明

在这里阅读更多

以身作则:

考虑Collections class 中的sort()方法,它同时使用extendssuper

    public static <T extends Comparable<? super T>> void sort(List<T> list){...}

所以

为什么<T extends Comparable<...>>因为我们需要列表项 ( T ) 作为Comparable接口的子类。

为什么Comparable<? super T> Comparable<? super T>因为我们允许Comparable类型是 T 的任何超类型的 Comparable

考虑

interface Comparable<T>{
    public int compareTo(T o);
}

public static <T extends Comparable<? super T>> void sort(List<T> list){...}


public static <T extends Comparable<T>> void sort2(List<T> list){...}


class A implements Comparable<A>{
    @Override
    public int compareTo(A o) {
        ...
    }
}

class B extends A {
}

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

    sort(listA);  //ok
    sort(listB);  //ok
    
    sort2(listA); //ok
    sort2(listB); //Error

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM