列表与列表<对象> </对象>

时间:2011-07-21 21:56:12

标签: java generics

为什么我们在使用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));
    }
}

为什么?

6 个答案:

答案 0 :(得分: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)需要{{1}时,它们就不会没有泛型类型参数。

编辑:

在评论中你提到了改变

List

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

这样

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,一个类型为T,并将值添加到列表中。

答案 1 :(得分:5)

List某些您不知道的类型的列表。它可以是List<String>List<Integer>等 它实际上等同于List<?>List<? extends Object>,但记录该事实。它仅支持向后兼容性。

List<Object>是一个对象列表 任何类型的任何对象都可以放在其中,与List<String>相反,例如,它只接受字符串。

所以不,他们不是一回事。

答案 2 :(得分:3)

List<Object>实际上并不比List更安全。但是,代码中的Object确实意味着意图。当其他人稍后查看它时,他们可以看到您故意选择Object作为类型,而不是想知道您是否忘记放置类型或存储其他内容并将其类型化为其他地方。

由于代码的读取次数超过了写入次数,因此稍后对代码意图的提示非常有价值。

答案 3 :(得分:1)

当您使用List而不是List<Object>时有编译器警告的原因是当您有List时,编译器不知道它是什么类型的List,所以虽然您可以将其视为List<Object>,但编译器无法确保在代码中的某个其他位置未将其设置为引用List<String>并且无法检查泛型的类型安全性。这实际上是编译器警告 - 它说它不能帮助确保泛型的类型安全性,并且它也不会在运行时发生(直到稍后在代码中存在实际的强制转换)。 / p>

答案 4 :(得分:0)

就目的而言,你可以说它们是一样的。但是大概你几乎从来没有用List的纯实例填充Object。他们是String或其他什么。在这个例子中,List<Object>在技术上使用泛型,但并没有真正利用它。因此,它丢失了泛型的编译时类型检查。

答案 5 :(得分:0)

Type Erasure是一个答案,并且与Java 1.5之前的版本具有向后兼容性,如果是第一个,则更加严格。

泛型被引入Java语言,以在编译时提供更严格的类型检查并支持泛型编程。为了实现泛型,Java编译器将类型擦除应用于:

将通用类型中的所有类型参数及其边界或对象替换为无边界的对象。因此,产生的字节码仅包含普通的类,接口和方法。 必要时插入类型转换,以保持类型安全。 生成桥接方法以在扩展的泛型类型中保留多态。 类型擦除可确保不会为参数化类型创建新的类;因此,泛型不会产生运行时开销。