使用通配符的Java集合

public static void main(String[] args) { List mylist = new ArrayList(); mylist.add("Java"); // compile error } 

上面的代码不允许您向列表中添加元素,并且通配符只能用作方法中的签名,同样不能用于添加,而只能用于访问。 在这种情况下,上述目的是什么?

假设您有一个接口和两个类:

 interface IResult {} class AResult implements IResult {} class BResult implements IResult {} 

然后你有一些类返回一个列表作为结果:

 interface ITest { List getResult(); } class ATest implements ITest { // look, overridden! List getResult(); } class BTest implements ITest { // overridden again! List getResult(); } 

当你需要“协变返回”时,它是一个很好的解决方案,但你返回集合而不是你自己的对象。 最重要的是,当您独立于ITest界面使用ATest和BTest时,您不必投射对象。 但是,在使用ITest界面时,您无法向返回的列表添加任何内容 – 因为您无法确定列表实际包含的对象类型! 如果允许,你可以将BResult添加到List (以List <?extends T>返回),这没有任何意义。

所以你必须记住这个:List <? extends X>定义一个可以轻松覆盖的列表,但它是只读的。

在他的书“伟大的有效Java”(第二版)中,Joshua Bloch解释了他所谓的使用generics的生产者/消费者原则。 Josh的解释应该告诉你为什么你的例子不起作用(编译)……

第5章(generics)可以在这里免费获得: http : //java.sun.com/docs/books/effective/generics.pdf

有关该书(和作者)的更多信息,请访问: http : //java.sun.com/docs/books/effective/

对于使用通配符的javagenerics,假设您只是从中读取,则允许上述声明。

您不能添加/写入它,因为必须在编译时剥离所有generics类型,并且在编译时,编译器不知道List只是字符串,(它可能是包括字符串在内的任何对象! )

但是,您可以从中读取,因为它们至少是对象。 java集合中不允许混合使用不同的类型以保持清洁和易懂,这有助于确保它。

有界通配符类型的要点是它们在方法签名中的使用,以增加API灵活性。 例如,如果您实现了一个通用的Stack ,那么您可以提供一种方法将大量元素推送到堆栈,如下所示:

 public void pushAll(Iterable elements) { for(E element : elements){ push(e); } } 

与没有通配符的pushAll(Iterable elements)签名相比,这具有以下优点:它允许将E的子类型的集合传递给该方法 – 通常这是不允许的,因为Iterable是违反直觉,不是Iterable的子类。

这有效:

 List mylist = new ArrayList(); mylist.add("Java"); // no compile error 

来自O’Reilly的Java Generics :

Get和Put原则:当你只获得结构的值时使用扩展通配符,当你只将值放入结构时使用超级通配符,并且不使用你得到和放置的通配符。

List List ,它与List相同,实现了对所有类型ListListList等进行泛化的目的(所以所有类型都有一个合适的类型代替? )。 可以将所有这些类型的值分配给List类型的变量(它与List !不同)。

通常,您不能将字符串添加到此类列表中。 但是,您可以从列表中读取Object ,并可以向其中添加null 。 您还可以计算列表的长度等。这些操作可以保证适用于每种类型。

有关通配符的详细介绍,请参阅将通配符添加到Java编程语言中 。 这是一篇学术论文,但仍然非常容易获得。

Javagenerics:集合中的通配符

  1. 扩展

今天我将向您解释外卡如何有用。 理解这个概念有点困难

现在假设你有抽象类,并且你有一个名为paintObject()的抽象方法。

 Now you want to use different type of collection in every child class. 

以下是AbstractMain方法。

这里我们采用了这个Abstract Main方法的步骤

我们创建了抽象类

2.在参数中我们定义了T(您可以使用任何字符) – 在这种情况下,无论哪个类实现此方法,它都可以使用任何类型的类。 恩。 类可以实现类似public void paintObject(ArrayList对象)或public void paintObject(HashSet对象)的方法

3.我们还使用了E extends MainColorTO – 在这种情况下,E扩展了MainColorTo – 它显然意味着你想要使用哪个类必须是MainColorTo的子类

4.我们定义了名为paintObject的抽象方法(T对象,E对象) – 现在这里有哪个类是实现方法,方法可以在第一个参数上使用任何类,第二个参数方法必须使用MainColorTO类型

 public abstract class AbstractMain { public abstract void paintObject(T Object,E TO); } 

现在我们将在上面的类ex上扩展上面的抽象类和实现方法。

 public class MainColorTO { public void paintColor(){ System.out.println("Paint Color........"); } } public class RedTO extends MainColorTO { @Override public void paintColor() { System.out.println("RedTO......"); } } public class WhiteTO extends MainColorTO { @Override public void paintColor() { System.out.println("White TO......"); } } 

现在我们将举两个例子。

1.PaintHome.java

 public class PaintHome extends AbstractMain { @Override public void paintObject(ArrayList arrayList,RedTO red) { System.out.println(arrayList); } } 

现在在上面的PaintHome.java中,您可以检查我们在第一个参数中使用了ArrayList(因为我们可以使用任何类),在第二个参数中我们使用了RedTO(它扩展了MainColorTO)

2.PaintCar.java

 public class PaintCar extends AbstractMain{ @Override public void paintObject(HashSet Object,WhiteTO white) { System.out.println(Object); } } 

现在在上面的PaintCar.java中你可以检查我们在第一个参数中使用了HashSet(我们可以接受任何类),在第二个参数中我们使用了WhiteTO(扩展了MainColorTO)

要记住的Ponint你不能在类级别使用super关键字,只能在类级别定义中使用extends关键字

 public abstract class AbstractMain { public abstract void paintObject(P Object,E TO); } 

上面的代码会给你编译错误。