我有一个简单的通用列表类。我试图实现这一点:给定一个列表实例,我们只知道它包含类的特定子类的实例,并给定该类的实例,如果它是实例,则将此实例添加到列表中包含(子类)类型,否则抛出异常(例如,ClassCastException)。我尝试了以下方法:
class MyList<T>
{
private Class<T> genericClass;
private List<T> list = new ArrayList<>();
public MyList(Class<T> genericClass)
{
this.genericClass = genericClass;
}
public void add(T elem)
{
list.add(elem);
}
//...
public Class<T> getGenericParamClass()
{
return genericClass;
}
}
class A{}
class B extends A{}
class C extends A{}
class Program
{
public static void main(String... args)
{
MyList<B> list1 = new MyList<>(B.class);
MyList<C> list2 = new MyList<>(C.class);
MyList<? extends A> ls = checkStuff() ? list1 : list2;
ls.add(ls.getGenericParamClass().cast(lotsOfStuff())); //ERROR ?!!
}
static boolean checkStuff()
{
Random random = new Random();
return random.nextBoolean();
}
static A lotsOfStuff()
{
return new B();
}
}
我认为给定一个Class对象,其类型参数与方法的参数类型相同,我可以使用前者来转换,以便能够将它传递给后者。唉,似乎我不能:我得到一个编译时错误!
我可以把仿制药扔出窗外,完全取消选中,然后说:
A val = lotsOfStuff();
if (myList.getGenericParamClass().isInstance(val))
ls.add(val)
else
throw new SomeException();
但是,这可能会产生比它解决的问题更多的问题,而且它也会让我感到烦恼。
我在这里遗漏了什么,或者我想出来的方式根本不可能?
修改: 我完全理解为什么这样的事情不起作用:
List<? extends Number> abc=new ArrayList<Integer>(); abc.add(new Integer(10));
但在我看来,以下传递性成立:add()参数的类型Is-The-Same-作为MyList的类型参数Is-The-Same-作为getGenericParamClass返回的Class的类型参数()Is-The-Same-作为该类的cast()方法的返回类型。我(作为人类)可以知道那些未知类型是相同的,因为我从同一个对象中获取它们。
我的逻辑是否存在错误,或者这是Java的限制?
答案 0 :(得分:2)
编译错误是:
在MyList类型中添加(捕获#2-of?extends A)的方法不适用于参数(捕获#3-of?extends A)
要理解该消息,请回想一下? extends A
代表未知类型,即A
的子类型。该编译器无法知道? extends A
返回的lotsOfStuff()
与? extends A
方法所期望的MyList.add
相同(或子类型),事实上,您的程序并不确定是这种情况(因为lotsOfStuff()
总是会返回B
,即使它应该添加到C
列表中。)
为了表示两者属于同一类型,我们必须使用类型参数。获得一个的最简单方法是移动代码进行转换并抛入类MyList<T>
(已经有一个合适的类型参数),例如通过添加以下方法:
void addOrThrow(Object o) {
add(genericClass.cast(o));
}