上面是一个Gen1泛型类.下面是Gen2类,它扩展了Gen1
你已经把问题埋在这里了.在Java中,您不仅仅继承父类的类型变量.如果您想让gen2
也继承‘这个类是参数化的;它有一个类型变量’这一概念,您需要这样声明它:
public class gen2<T> extends gen1<T> {}
您在代码片段中执行的操作是extends gen1
,这意味着您已经删除了泛型:您现在处于遗留/原始模式,all generics and all benefits of it, for all uses of that entire class everywhere已经消失.不应该使用遗留/原始版本,但您在这里使用它.在这种情况下,类型推理如何工作?答案很简单,不是这样的.
即使在使用原始/遗留版本的情况下,似乎不会对编译器的类型推断能力产生负面影响,也会丢失类型推断.这不是它的工作方式:您处于原始模式,因此关闭了all个基于泛型的推理,无论是否需要这样做才能使原始/遗留工作.
一百零二
因为您忽略了它,所以Gen2的所有S签名中的所有T
,包括从Gen1继承的签名,都变成了它的上限,即Object,所以,meth2(someInstanceOfGen1)
就可以了,因为meth2
方法需要作为参数1 Object
,这就是您要传递的.
new gen1(Integer.valueOf(12))
也可以很好地工作--new gen1
表示您处于原始模式(您想要new gen1<RuntimeException>(Integer.valueOf(12))
--这会生成编译时错误)--如果您想要类型推断,您想要new gen1<>(Integer.valueOf(12))
,这将推断您打算编写new gen1<Integer>
--这是否可以取决于您对它做了什么.在这里,您将其传递给meth2
,这很好,因为由于class gen1 extends gen2
,meth2
是原始/遗留模式.
那么,你应该怎么做呢?
public class Gen1<T> {
T var1;
public gen1(T arg1)
{
var1=arg1;
}
void meth1(T arg1){
}
void meth2(gen1<? extends RuntimeException> arg)
{
}
}
public class gen2<T> extends gen1<T> {
public gen2(){
super(new Exception());
meth2(new gen1<>(Integer.valueOf(12))); // compile time error here
}
}
你说‘这应该抛出一个错误’,这也不是它的工作方式,你对这个东西理解得相当彻底.
Generics are ENTIRELY a figment of the compiler's imagination.
在运行时,泛型do not exist in any way.java.exe
的人不知道他们是什么.在从.java
文件到.class
文件的转换中,大多数泛型都完全消失了(javac使用它们来生成警告、错误和注入静默强制转换,然后大部分消除它们),类文件中保留的少数泛型被运行库视为注释-它只是跳过它们,不知道它们的含义,也不会以任何方式影响运行库所做的事情.它们只存在于类文件中,以便javac
知道当您在编译某些代码时将类文件放在类路径上时要做什么,仅此而已.
而"抛出"完全是运行时的事情.编译器不会"抛出"任何东西.它会产生编译器错误或警告.因此,为什么不能将违反泛型描述为导致"扔"东西.它们生活在不同的领域;一个只关注编译器,一个只关注运行时.
在最好的情况下,陷入泛型警告(编译器明确告诉您:由于使用原始类型和/或强制转换为泛型而无法正确完成,因为强制转换是运行时的事情,而泛型是编译时的事情,我实际上不能保证类型安全),将导致在运行时在包含零强制转换的行上抛出ClassCastException
个错误(因为CCEx是由编译器悄悄注入的强制转换导致的).