列表 vs 对象列表

15
为什么使用 List 时会失去类型安全而使用 List<Object> 时不会?它们不是基本上相同的东西吗? 编辑:我发现以下代码会编译错误
public class TestClass
{
    static void func(List<Object> o, Object s){
        o.add(s);
    }

    public static void main(String[] args){
        func(new ArrayList<String>(), new Integer(1));
    }
}

然而这样并不会

public class TestClass
{
    static void func(List o, Object s){
        o.add(s);
    }

    public static void main(String[] args){
        func(new ArrayList<String>(), new Integer(1));
    }
}

为什么?


2
第二个代码能够编译的直接原因是为了让在Java 5之前编写的代码可以被5及以后版本编译。 - Affe
6个回答

8
List是一种某种类型的列表,可能是List<String>List<Integer>等。它与List<?>List<? extends Object>几乎等效,但它没有记录这个事实。它仅支持向后兼容。 List<Object>是一个对象列表,任何类型的任何对象都可以放在其中,与List<String>不同,例如,它只接受字符串。
所以,它们不是同一件事。

5

使用 List 而不是 List<Object> 时,为什么会失去类型安全性?它们不是基本上相同的东西吗?

不,它们并不相同。

如果您提供一个 API,

class API {
  static List<Object> getList() { ... }
  static void modifyList(List<Object> l) { ... }
}

如果客户端使用不当

List<Integer> list = API.getList();
API.modifyList(list);
for (Integer i : list) { ... }  // Invalid

当你的API指定List<Object>时,编译时会出现错误,但当API.getList()返回一个没有泛型类型参数的List并且API.modifyList(list)接受一个List时,他们不会出错。
编辑:
在评论中提到更改。
void func(List<Object> s, Object c) { s.add(c); }

to

void func(List s, Object c) { s.add(c); }

为了使……,以便……
func(new List<String>(), "");

这是与类型安全不符的。做到类型安全的方法是

<T> void func(List<? super T> s, T c) { s.add(c); }

这基本上是在说func是一个参数化函数,它接受一个类型可以是T的任何超类的List和一个类型为T的值,并将该值添加到列表中。


是的,我找到了一个类似的情况。当我有以下代码时程序无法编译: func(List<Object> s, Object c){ s.add(c)} 并调用 func(new ArrayList<String>(), new Integer(1));.当我将方法更改为 func(List s, Object o) 并以相同的方式再次调用它时,它可以编译。 - Varun Achar
没有泛型,for循环也无法编译。 - user802421
@Varun,一个List<String>不是一个List<Object>,因为当myList是一个List<Object>时,myList.add(Boolean.FALSE)是有效的,但当它是一个List<String>时就无效了。也许你的函数应该是<T> void func(List<? super T> s, T c) { s.add(c); } - Mike Samuel
@user102008,正确。但那不够通用。然后你不能这样做:List <Object> heterogeneousList = ...; func(heterogeneousList,“foo”); ,因为“ foo ”使T在该调用网站绑定到 String,但 hetergeneousList的类型为 List <Object>,它不是List <T>。如果我想尽可能通用,我应该使用 Collection而不是 List - Mike Samuel
@Mike Samuel:是的,你可以试试。 - user102008
显示剩余2条评论

3
一个 List<Object> 并不比一个 List 更加类型安全。然而,代码中的 Object 暗示了意图。当其他人稍后查看时,他们可以看到您有意选择了 Object 作为类型,而不是想知道您是否忘记了放置类型或者存储了其他东西并在其他地方进行类型转换。
由于代码被读取的次数比写入的次数多,代码意图的提示在以后可能非常有价值。

1

当您使用List而不是List<Object>时,编译器出现警告的原因是当您拥有一个List时,编译器无法知道它是什么类型的 List,所以虽然您可以将其视为List<Object>,但编译器不能保证在代码的某些其他点上它没有被设置为引用List<String>。泛型的类型安全性无法检查,这就是编译器警告的真正原因。即它无法帮助确保泛型的类型安全性,并且这也不会发生在运行时(除非在代码中的某个后续点上有实际的转换)。


0

在这里,你可以说它们是同一件事。但是假设你几乎从不使用纯粹的Object实例填充List。它们是String或其他东西。在这个例子中,List<Object>在技术上使用了泛型,但并没有真正利用它。因此,它失去了泛型的编译时类型检查。


0

类型擦除是其中一种答案,可以实现向后兼容到Java 1.5以前的版本,并且在第一个答案中能够进行更严格的类型检查。

泛型被引入Java语言,提供编译时更严格的类型检查和支持通用编程。为了实现泛型,Java编译器对以下内容进行类型擦除:

将泛型类型中的所有类型参数替换为它们的边界或Object(如果类型参数无限制)。因此,生成的字节码仅包含普通类、接口和方法。 必要时插入类型转换以保留类型安全性。 生成桥接方法以保留扩展泛型类型的多态性。 类型擦除确保不会为参数化类型创建新的类;因此,泛型不会产生运行时开销。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接