mir*_*sif 37 java generics generic-method
我正在学习Java中的泛型,我接近了一段非常有趣的代码.我知道在Java中将一种类型的列表添加到另一种类型是违法的.
List<Integer> integerList = new ArrayList<Integer>();
List<String> stringList=integerList;
Run Code Online (Sandbox Code Playgroud)
所以在第二行我得到一个编译时错误.
但是如果我在这样的类中创建一个泛型方法,
class GenericClass <E>{
void genericFunction(List<String> stringList) {
stringList.add("foo");
}
// some other code
}
Run Code Online (Sandbox Code Playgroud)
并且在主类中调用带有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));
}
}
Run Code Online (Sandbox Code Playgroud)
输出
100
foo
为什么我没有收到任何错误?
Dan*_*iel 23
您没有得到任何编译时错误,因为以GenericClass<E>原始方式使用:
GenericClass genericClass = new GenericClass();,
你实际上告诉编译器禁用泛型类型检查,因为你不在乎.
所以 :
void genericFunction(List<String> stringList)
变
void genericFunction(List stringList) 对于编译器.
您可以尝试以下操作:GenericClass<?> genericClass,您会立即注意到编译器会发现泛型使用不当,并会显示错误:
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());
Run Code Online (Sandbox Code Playgroud)
,你会得到一个错误:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer.
Bra*_*raj 19
您已将Generic与原始类型混合使用.它将编译正常,但在运行时它可能会失败,因为通用信息在运行时丢失.
应该使用Generic在编译时跟踪这些错误.
更好地解释什么是原始类型,为什么我们不应该使用它?详细地.
警告:类型安全:该方法genericFunction(List)属于原始类型GenericClass.GenericClass<E>应参数化对泛型类型的引用.
如果有两个具有相同名称且具有不同Generic类型List的方法,则会导致编译时错误.如果方法参数可以通过下面的示例代码证明,编译器无法解析Generic类型.
示例代码:(编译器错误 - 不是有效的重载方法)
void genericFunction(List<String> stringList){...}
void genericFunction(List<Integer> stringList){...}
Run Code Online (Sandbox Code Playgroud)
进行一些更改并再次尝试:
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
Run Code Online (Sandbox Code Playgroud)
以这种方式创建方法
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
}
Run Code Online (Sandbox Code Playgroud)
ski*_*iwi 12
这种情况发生了,因为(对我来说非常令人惊讶)你已经关闭了整个 GenericClass类的泛型类型检查.
您需要注意,首先,您在构造一个没有类型参数的泛型类:
GenericClass genericClass = new GenericClass();
Run Code Online (Sandbox Code Playgroud)
而且,正因为如此,您的以下代码:
class GenericClass<E> {
void genericFunction(List<String> stringList) {
stringList.add("foo");
}
// some other code
}
Run Code Online (Sandbox Code Playgroud)
精炼到:
class GenericClass {
void genericFunction(List stringList) {
stringList.add("foo");
}
// some other code
}
Run Code Online (Sandbox Code Playgroud)
请注意,这List 也成为一种原始类型,这对我来说相当令人惊讶.
您可以在此处找到完整的答案,参考JLS:https://stackoverflow.com/a/662257/2057294,如Jon Skeet所述.
我认为发生这种情况是公平的(虽然不是你所期望的),因为如果你决定使用原始类型,那么假设你使用的是Java 4或更低版本,并且无论如何都无法访问泛型,所以它也可以不提供它们用于不涉及被删除的类的泛型类型的方法.
添加到其他答案.
实际上,您正在将原始(未参数化)类型与参数化类型混合在一起,这会导致类型丢失,并且genericFunction在类型安全检查不允许的情况下传递给明显正确的参数.
剩下的问题上,为什么该List<String>类型以unparameterized失去了GenericClass对象.编译器在您的情况下"禁用"类型检查的原因是类型擦除机制,它说(简单来说)您的原始对象属于具有以下内容的类:
class GenericClass {
void genericFunction(List stringList) {
stringList.add("foo");
}
// some other code
}
Run Code Online (Sandbox Code Playgroud)
正如你看到不作任何类型的检查,凡在列表中的内容传递(它会删除类型参数无处不在).更重要的是,类型擦除也会从integerList变量中删除参数化类型,这使得它完全适合作为genericFunction方法的参数.
因此,正如其他人所指出的那样,最好是帮助Java保持其安全类型检查机制的完整性:
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));
Run Code Online (Sandbox Code Playgroud)
但是,如果你这样做,编译器就会完成它的工作,并对你进行bazooka.
在这里阅读有关类型擦除的更多信息.这是一个很好的功能,允许进行泛型类型检查,同时仍然保持向前兼容Java版本的向后兼容性.