如果我有这样的抽象类:
public abstract class Item
{
private Integer value;
public Item()
{
value=new Integer(0);
}
public Item(Integer value)
{
this.value=new Integer();
}
}
一些来自Item的类如下:
public class Pencil extends Item
{
public Pencil()
{
super();
}
public Pencil(Integer value)
{
super(value);
}
}
我不明白为什么我不能使用泛型来调用构造函数:
public class Box <T extends Item>
{
T item;
public Box()
{
item=new T(); // here I get the error
}
}
我知道有可能有一个没有构造函数的类型,但这种情况是不可能的,因为Pencil有没有参数的构造函数,而Item是抽象的。
但是我从eclipse中得到了这个错误:
无法实现类型T
我不明白为什么,以及如何避免这种情况?
答案 0 :(得分:13)
这是因为Java使用擦除来实现泛型,请参阅:
引用上述维基百科文章中的相关部分:
在编译时检查泛型的类型正确性。然后在称为类型擦除的过程中移除泛型类型信息。
由于类型擦除,无法在运行时确定类型参数。
因此,实例化参数化类型的Java类是不可能的,因为实例化需要调用构造函数,如果类型未知,则该构造函数不可用。
你可以通过自己提供课程来解决这个问题。这在这里有很好的解释:
答案 1 :(得分:11)
无法使用Java类型系统强制类层次结构具有其子类构造函数的统一签名。
考虑:
public class ColorPencil extends Pencil
{
private Color color;
public ColorPencil(Color color)
{
super();
this.color=color;
}
}
这使得ColorPencil成为有效的T(它扩展了Item)。但是,此类型没有no-arg构造函数。因此,T()是荒谬的。
要做你想做的事,你需要使用反射。您无法从编译时错误检查中受益。
答案 2 :(得分:1)
T
是您的类将处理的实际类型的别名,例如,如果您实例化Box<Item>
,那么T
实际上只是Item
的别名。当您声明T extends Item
时,您就会知道T
至少具有与Item
相同的界面,因此您可以将其视为一个。
我认为你真正想做的不是在item
中实例化Box
字段,而是实现一些方法让你操作该字段。
public class Box<T extends Item> {
private T item;
public T getItem() {
return this.item;
}
public void setItem(T item) {
return this.item = item;
}
}
答案 3 :(得分:0)
因为在运行时T的类型是未知的。
答案 4 :(得分:0)
使用T来调用构造函数是不可能的,因为如果它可能比编译后可能会得到这样的代码:
public class Box{
Object item;
public Box(){
item = new Object();
}
}
因此,如果您使用此代码并传递一些对象,而不是您期望的某些特定类型的构造函数被调用,而是获得Object构造函数。
答案 5 :(得分:0)
您可以放置一个抽象方法,以便实现类可以确定如何构造新对象。
public abstract T constructT();
而不是调用
T t = new T()
你会打电话给
T t = constructT();
在您的实施调用中,它将创建为:
new Box<Integer>() {
public Integer constructT(){ return new Integer(); }
}