我知道无法做到:
new T
其中T
由于类型擦除而属于泛型类型。
为什么可以这样做new A<T>
,其中T是通用的?例如,为什么以下编译?
static class A<T>{
T t;
A(T t){
this.t=t;
}
}
static class B<T>{
A<T> create(T t){
return new A<T>(t);
}
}
public static void main (String[] args) throws java.lang.Exception
{
A<Integer> a = new B<Integer>().create(3);
}
答案 0 :(得分:6)
因为A<Integer>
在编译和运行时是一个定义良好的类型。
在概念上,中间步骤之后:
new B<Integer>()
您对.create(3)
的致电等同于致电:
static class B {
A<Integer> create(Integer t){
return new A<Integer>(t);
}
}
擦除后,A<Integer>
变为A
,这是一种有效的类型。
你不能new T
因为T
不是定义明确的类型,它只是一个标记。如果确实需要,可以使用以下内容获取相应的类并实例化T
类型的对象:
public class GenericFactory {
<T> T create(Class<T> c) throws Exception {
return c.newInstance();
}
}
并使用它:
new GenericFactory().create(Integer.class);
是的,那太丑了。
答案 1 :(得分:0)
由于类型擦除,您可以实例化new A<T>()
。如果Java将Generics实现为可再生,那么如果没有用于查找T
的运行时绑定的反射,这将无法工作。
通过扩展,它是由于对象类型的非泛型方面被实现(可以用getClass()
访问),编译器不可能发出这样的字节码,它能够实例化任何键入T,具体取决于呼叫站点。
答案 2 :(得分:0)
您没有创建通用模板的新实例。您正在使用通用模板创建对象实例。
new A<T>(t);
创建一个新的A
,这不是通用的。您不一定知道编译时T
会是什么,但您知道该对象将是A
。事实上,它的美妙之处在于你不必预知T
将会是什么,这可以让你创建如此强大的代码,这些代码将是可重用的。想象一下,如果你实现了Stack
,那么你对实际的内容类型不感兴趣。您有兴趣好好处理top
,pop
,push
操作。如果您不必预测元素是Integer
,String
还是其他类型,您可以优雅地编写适用于许多不同类的通用代码。
如果我必须预测T
是什么,那么我宁愿说它是Object
并且能够处理任何对象。事实上我相信这样的事情会在后台发生。另外,另一种选择是使用reflection
,但这根本就不干净。