将整数列表分配到string列表中

我正在学Java的generics,我接近了一个非常有趣的代码。 我知道在Java中将一种types的列表添加到另一种types是非法的。

List<Integer> integerList = new ArrayList<Integer>(); List<String> stringList=integerList; 

所以在第二行我得到一个编译时错误。
但是如果我在这样的类中创build一个generics方法,

 class GenericClass <E>{ void genericFunction(List<String> stringList) { stringList.add("foo"); } // some other code } 

而在主类调用与Integer列表的方法我没有得到任何错误。

 public class Main { public static void main(String args[]) { GenericClass genericClass=new GenericClass(); List<Integer> integerList= new ArrayList<Integer>(); integerList.add(100); genericClass.genericFunction(integerList); System.out.println(integerList.get(0)); System.out.println(integerList.get(1)); } } 

产量
100
FOO

为什么我没有得到任何错误?

你有混合通用与原始types 。 它可以很好地编译,但是在运行时可能会失败,因为通用信息在运行时会丢失。

在编译时应该使用generics来追踪这些错误。

什么是原始types,为什么我们不应该使用它? 详细。


警告:types安全:方法genericFunction(List)属于原始typesGenericClass 。 对genericsGenericClass<E>引用应该被参数化。

如果你有两个具有不同GenerictypesList的相同名字的方法,那么它会导致编译时错误。 在下面的示例代码可以certificate的方法参数的情况下,编译器无法parsinggenericstypes。

示例代码:(编译器错误 – 不是有效的重载方法)

 void genericFunction(List<String> stringList){...} void genericFunction(List<Integer> stringList){...} 

进行一些更改并再次尝试:

 class GenericClass <E>{ void genericFunction(List<E> stringList) { ... } // some other code } ... GenericClass<String> genericClass=new GenericClass<String>(); // Genreric object List<Integer> integerList= new ArrayList<Integer>(); integerList.add(100); genericClass.genericFunction(integerList); // compile time error 

以这种方式创build方法

 class GenericClass<E> { private List<E> list = new ArrayList<E>(); public void addAll(List<E> newList) { list.addAll(newList); } public void add(E e) { list.add(e); } public E get(int index) { return list.get(index); } // some other code } 

您没有得到任何编译时错误,因为通过以原始方式使用GenericClass<E>

GenericClass genericClass = new GenericClass();

你实际上告诉编译器禁用genericstypes检查,因为你不在乎。

所以 :

void genericFunction(List<String> stringList)

编译器的void genericFunction(List stringList)

你可以尝试下面的代码: GenericClass<?> genericClass ,你会立刻注意到编译器意识到这个generics不好用,它会告诉你错误:

The method genericFunction(List<String>) in the type GenericClass<capture#1-of ?> is not applicable for the arguments (List<Integer>)

此外,如果您尝试在运行时获取第二个位置对象的类:

 System.out.println(integerList.get(1).getClass()); 

,你会得到一个错误: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

发生这种情况,因为(相当令人惊讶的是我)你已经closures了整个 GenericClass类的genericstypes检查。

你需要知道,你首先在这里构造一个没有types参数的generics类:

 GenericClass genericClass = new GenericClass(); 

而且,由于这个原因,你的下面的代码是:

 class GenericClass<E> { void genericFunction(List<String> stringList) { stringList.add("foo"); } // some other code } 

精炼到:

 class GenericClass { void genericFunction(List stringList) { stringList.add("foo"); } // some other code } 

请注意, List 成为一个原始types,这是我相当惊讶。

你可以在这里find完整的答案,参考Jon Skeet解释的JLS: https ://stackoverflow.com/a/662257/2057294。

我认为这是公平的(尽pipe不是你所期望的),因为如果你决定使用原始types,那么假定你正在使用Java 4或更低版本,并且根本不可能访问generics,所以它也可以不提供他们的方法,而不涉及被删除的类的genericstypes。

添加到其他答案。

事实上,你正在混合使用参数化types的原始(未参数化)types,这会导致types丢失,并且在types安全检查不应该允许的情况下将明显正确的parameter passing给genericFunction

问题仍然是为什么 List<String>types在未参数化的GenericClass对象中丢失。 在你的情况下,编译器“禁用”types检查的原因是types擦除机制,它说(简单来说)你的原始对象属于一个具有以下内容的类:

 class GenericClass { void genericFunction(List stringList) { stringList.add("foo"); } // some other code } 

正如你所看到的,不会对所传递的列表内容进行任何types的检查(它会擦除所有types的参数)。 更重要的是,types擦除也会从integerListvariables中擦除您的参数化types,这使得它完全适合您的genericFunction方法的参数。

因此,正如其他人指出的那样,最好能帮助Java保持安全的types检查机制:

  GenericClass<?> genericClass=new GenericClass<Integer>(); List<Integer> integerList= new ArrayList<Integer>(); integerList.add(100); // no-no here genericClass.genericFunction(integerList); System.out.println(integerList.get(0)); System.out.println(integerList.get(1)); 

但是如果你这样做了,那么编译器就会完成它的工作,然后把火箭筒放在你身上。

在这里阅读types擦除更多。 这是一个很好的function,允许进行genericstypes检查,同时仍然保持与预genericsJava版本的向后兼容性。

 class GenericClass <E>{ void genericFunction(List<String> stringList) { stringList.add("foo"); } // some other code } 

当你在类名后面写。它指定了types参数。它是一个通用的function。它引入了typesvariablesE,它可以在类中的任何地方使用。 typesvariables可以是您指定的任何非基本types:任何类types,任何接口types,任何数组types或甚至另一个typesvariables。