今天编译这段代码时我感到很惊讶:
class GenericClass<T> {
public void emptyMethod(T instance) {
// ..
}
public void print(T instance) {
System.out.println(instance);
}
}
public class Main {
public static void main(String[] args) {
GenericClass first = new GenericClass();
System.out.println("Wow");
first.emptyMethod(10);
first.print(16);
}
}
编译器发出警告(类型安全:方法emptyMethod(Object)属于原始类型GenericList。对泛型类型GenericList的引用应该参数化),但无论如何它不会导致编译器错误并且它运行'正常' (至少提供的打印方法)。正如我所理解的,编译器使用object作为类型参数,但我觉得它反直觉。为什么编译器会这样做呢?为什么它不要求我指定类型参数?
答案 0 :(得分:7)
基本上你正在使用 raw 类。
回想一下在Java中首次引入泛型时:有一堆代码已经使用List
,ArrayList
等等。为了避免破坏所有代码,但仍然重用现有的类,原始类型被引入 - 它基本上使用泛型类型,就好像它不是一个。
正如你所看到的,你得到一个警告 - 所以值得避免 - 但这是它被允许的主要原因。
有关详细信息,请参阅JLS的section 4.8,其中包括:
原始类型与通配符密切相关。两者都基于存在类型。原始类型可以被认为是通配符,其类型规则是故意不合理的,以适应与遗留代码的交互。从历史上看,原始类型先于通配符;它们最初是在GJ中引入的,并在文章中描述了过去的未来安全:Gilad Bracha,Martin Odersky,David Stoutamire和Philip Wadler在Java编程语言中添加了通用性,参见ACM会议论文集 - 面向编程,系统,语言和应用(OOPSLA 98),1998年10月。
答案 1 :(得分:2)
您必须知道如何在Java中实现泛型。它们远非完美。 你必须记住,在运行时,一切都是一个对象。运行时没有类型。
添加了泛型以便在需要的地方增加安全性,但如果您不想使用它,则可以忽略警告并使用未参数化的实例。
但是,如果您希望java编译器帮助您进行类型安全,那么您可以参数化泛型类实例。例如,创建GenericClass后,编译器将不允许您将其与整数参数一起使用(first.emptyMethod(10)将无法编译)。如果你进行显式类型转换,你仍然可以使用整数参数。
因此,将其视为增加安全性的良好做法,只有遵守规则才能有效。