7

可能重复:
Map.get(Object key) 不是(完全)通用的
原因是什么为什么我们有 contains(Object o) 而不是 contains(E e)?

正如你们在这里看到的, E 类型的模板化 java.util.List 的contains方法没有被模板化:它Object取而代之。有谁知道为什么?
在什么情况下List<String>返回 true myList.contains(new OtherNonString())?如果我没记错的话,永远不要,除非它与之比较的对象具有 E 类型作为祖先(在我的字符串示例中,由于String是最终的,这是不可能的)

是否只是为了保持与前泛型版本的向后兼容性?我错过了一个有意义的用例吗?如果只是为了向后兼容,为什么不弃用contains(Object)并创建一个contains(E)?

编辑:
我的一些子问题之前已经回答过了。作为参考,还要检查这个问题

4

7 回答 7

7

如果只是为了向后兼容,为什么不弃用 contains(Object) 并创建一个 contains(E)?

因为contains(Object)和具有相同的擦除类型(如您在此代码示例contains(E)中所见),因此会导致编译错误。此外,弃用方法不是一种选择,当时的首要任务是让遗留代码工作。

于 2012-11-09T16:16:42.823 回答
2

因为这里不需要模板:这只会阻止一些测试,如果对象不属于所需的类,则无论如何该方法都会回答 false。

在您的代码中进行简单的测试检查函数的返回是否是布尔值比测试 try/catch 要简单得多。在编译时检查类型将允许发现错误的少数情况不值得开销。

于 2012-11-09T16:12:21.653 回答
2

这是因为该方法可以返回 true,即使参数的类型与列表类型不同。更准确地说,contains(Object o)如果列表包含元素 e,则返回 true,所以这e.equals(o)是真的。

例如,以下代码将打印 true,即使在 中l2不允许使用of 的类型list

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

ArrayList<String> l1 = new ArrayList<String>();
l1.add("foo");

list.add(l1);

LinkedList<String> l2 = new LinkedList<String>();
l2.add("foo");

System.out.println(list.contains(l2));

原因是不同的类 ArrayList 和 LinkedList 都继承了 AbstractList 的 equals 实现,不区分不同的子类。即使两个对象没有共同的超类,它们的 equals 实现也有可能相互识别。

于 2012-11-09T16:27:40.447 回答
1

原因之一可能是contains()不改变列表,因此不需要强制执行该类型。

从您拥有的链接:

如果此列表包含指定元素,则返回 true。更正式地说,当且仅当此列表包含至少一个元素 e 使得 (o==null ? e==null : o.equals(e))

于 2012-11-09T16:12:08.250 回答
1

是否只是为了保持与前泛型版本的向后兼容性?

不,这是由类型擦除处理的。

之所以这样,是因为该方法不需要是类型安全的,也不需要返回实际类型。

于 2012-11-09T16:19:55.010 回答
0

一个反例:

List<String> strings = Arrays.asList("hello", "world");
Object o = "hello";
System.out.println(strings.contains(o)); // true

如果该contains方法不允许Object引用作为参数,则无法编译上面的代码。但是,该o变量引用了 a 的实例,该实例String实际上包含在给定列表中。

的结果containsObject.equals(Object o)方法的结果确定,该方法还将其参数的类型定义为一般Object,原因相同:

String hello = "hello";
Object o = "hello";
System.out.println(hello.equals(o)); // true
于 2012-11-09T16:28:43.543 回答
0

Java 中的泛型是使用一种称为擦除的技术实现的。

  • 如果没有给出泛型类型,则该类型将替换为 Object。
  • 如有必要,如果给出了另一个泛型类型,则 java 编译器会创建类型转换为另一个对象。
  • 编译器还生成桥接方法以保留扩展泛型类型中的多态性。

这就是为什么在运行时编译的字节码中存在 nog 泛型类型的原因。

例如

public static <T> void printArray ( T [] inputArray ) {
 for ( T element : inputArray )
    System.out.printf("%s ", element) ;

 System.out.println();
}

编译器执行擦除后

public static void printArray ( Object [] inputArray ) {
    for ( Object element : inputArray )
      System.out.printf("%s ", element) ;

 System.out.println();
}

它们只是内存中这段代码的一个副本,在这个例子中所有 printArray 调用都会调用它。

这样做的原因是向后兼容。泛型最初是在 java 1.5 版中引入的。

在 java 版本 < 1.5 中,您定义了一个这样的列表:

List myList = new ArrayList();

而不是这样

List<Integer> myList = new ArrayList<Integer>();

To make sure that old code won't break that was already written the compiled class can not contain information about generics.

于 2012-11-09T16:33:12.093 回答