Java 泛型:列表、列表<对象>、列表<?>

2022-08-31 13:55:53

有人可以尽可能详细地解释以下类型之间的差异吗?

List
List<Object>
List<?>

让我更具体地说明这一点。我想什么时候使用

// 1 
public void CanYouGiveMeAnAnswer(List l) { }

// 2
public void CanYouGiveMeAnAnswer(List<Object> l) { }

// 3
public void CanYouGiveMeAnAnswer(List<?> l) { }

答案 1

正如其他帖子所指出的,您正在询问一种称为泛型的Java功能。在C++中,这称为模板。Java中的此功能通常比C++中的功能更容易使用。

让我从功能上回答你的问题(如果这不是OO讨论的顽皮词)。

在泛型之前,有像Vector这样的具体类。

Vector V = new Vector();

矢量可以容纳您提供给它们的任何对象。

V.add("This is an element");
V.add(new Integer(2));
v.add(new Hashtable());

它们通过将赋予它的所有值转换为对象(所有 Java 类的根)来实现此目的。当您尝试检索存储在 Vector 中的值时,您需要将该值转换回原始类(如果要对它执行任何有意义的操作)。

String s = (String) v.get(0);
Integer i = (Integer) v.get(1);
Hashtable h = (Hashtable) v.get(2);

铸造很快就会变老。不仅如此,编译器还会向您抱怨未经检查的强制转换。像这样的转换最紧迫的问题是,Vector的使用者必须在编译时知道其值的类,以便正确转换。如果Vector的生产者和它们的消费者彼此完全隔离(想想RPC消息),这可能是一个致命的问题。

输入泛型。泛型尝试创建强类型类来执行泛型操作。

ArrayList<String> aList = new ArrayList<String>();
aList.add("One");
String element = aList.get(0); // no cast needed
System.out.println("Got one: " + element); 

设计模式》一书鼓励读者从契约的角度来思考,而不是从具体的类型的角度来思考。将变量与其实现类分离是有智慧的(和代码重用)。

考虑到这一点,您可能会认为所有实现List对象都应该执行相同的一组操作:,,,等。稍加反思,您可以想象 List 操作的许多实现,它们以各种方式(例如 )服从 List 协定。但是,这些对象处理的数据类型与对其执行的操作正交。add()get()size()ArrayList

将它们放在一起,您将经常看到以下类型的代码:

List<String> L = new ArrayList<String>();

您应该将其理解为“L 是一种处理 String 对象的列表”。当您开始处理工厂类时,处理协定而不是特定实现至关重要。工厂在运行时生成各种类型的对象。

使用泛型非常容易(大多数时候)。

有一天,你可能会决定要实现自己的泛型类。也许你想写一个新的数据库抽象接口来消除各种数据存储之间的差异。定义该泛型类时,将用作将由方法操作的对象类型的占位符。<t>

如果您仍然感到困惑,请使用 List 的泛型类,直到您感到舒适为止。稍后,您可以更有信心地深入了解实现。或者,您可以查看 JRE 附带的各种 List 类的源代码。开源就是这样。

看看关于泛型的Oracle/Sun文档。干杯。


答案 2

用我自己的简单术语来说:

列表

将声明一个普通集合,可以保存任何类型,并将始终返回 Object。

列表<对象>

将创建一个列表,该列表可以保存任何类型的对象,但只能分配另一个 List<Object>

例如,这不起作用;

List<Object> l = new ArrayList<String>();

当然,您可以添加任何内容,但只能拉取对象。

List<Object> l = new ArrayList<Object>();

l.add( new Employee() );
l.add( new String() );

Object o = l.get( 0 );
Object o2 = l.get( 1 );

最后

列表<?>

将允许您分配任何类型,包括

List <?> l = new ArrayList(); 
List <?> l2 = new ArrayList<String>();

这将被称为未知的集合,并且由于未知的公分母是对象,您将能够获取对象(巧合)

当未知与子类结合使用时,它的重要性就来了:

List<? extends Collection> l = new ArrayList<TreeSet>(); // compiles

List<? extends Collection> l = new ArrayList<String>(); // doesn't,
// because String is not part of *Collection* inheritance tree. 

我希望使用Collection作为类型不会造成混乱,这是我想到的唯一一棵树。

这里的区别在于,l 是属于集合层次结构的未知集合


推荐