如何在Java运行时确保参数化类型的正确性



我有一个简单的通用列表类。我试图实现这一点:给定一个列表实例,我们只知道它包含一个类的特定子类的实例,并给定该类的实例,如果它是包含(子类)类型的实例,则将此实例添加到列表中,否则抛出异常(例如。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()的参数类型是相同的,MyList的类型参数是相同的,getGenericParamClass()返回的类的类型参数是相同的,该类的cast()方法的返回类型是相同的。我(作为人类)可以知道这些未知类型是相同的,因为我从同一个对象获得它们。

是我的逻辑有问题,还是这是Java的限制?

编译错误为:

方法add(capture#2-of ?类型MyList中的extends A)不适用于参数(捕获#3-of ?扩展)

要理解该消息,请记住,? extends A表示未知类型,该类型是A的子类型。该编译器无法知道lotsOfStuff()返回的? extends AMyList.add方法期望的? extends A相同(或其子类型),事实上,您的程序并不能确保这种情况(因为lotsOfStuff()总是返回B,即使它应该添加到C列表中。)

要表示两者是同一类型,必须使用类型形参。最简单的方法是将进行类型转换和抛出的代码移到类MyList<T>中(该类已经具有合适的类型参数),例如通过添加以下方法:

void addOrThrow(Object o) {
    add(genericClass.cast(o));
}

最新更新