“Set <! - ?extends Class <?extends Throwable - >&gt;”真?

时间:2011-11-02 15:30:55

标签: java generics collections

我一直在使用一个小的泛型方法来从vararg元素创建集合,例如

public <T> Set<T> createSet( T... elements ) { ...

但是,最近我遇到了编译器没有按照我的预期行事的情况。以下createSet()仅使用s3作品。

Set<Class<? extends Throwable>> s1 = createSet( Exception.class, RuntimeException.class );

Set<? extends Class<Throwable>> s2 = createSet( Exception.class, RuntimeException.class );

Set<? extends Class<? extends Throwable>> s3 = createSet( Exception.class, RuntimeException.class );

任何人都可以清楚地解释为什么s3有效以及我的思考可能出现的问题是关于s1 - 这是我的初始编码?感谢。

4 个答案:

答案 0 :(得分:10)

问题在于推理逻辑。如果您显式键入方法调用,s1工作正常(嗯,除了vararg警告):

Set<Class<? extends Throwable>> s1 = 
    this.<Class<? extends Throwable>>createSet( Exception.class, RuntimeException.class );

但默认情况下,给定参数的返回类型为Set<Class<? extends Exception>>(我想因为它是最具体的可能性)。你只需要在这里给编译器一个提示,因为没有它,它本质上是试图这样做:

Set<Class<? extends Exception>> temp = createSet(Exception.class, RuntimeException.class);
Set<Class<? extends Throwable>> s1 = temp;

这是不允许的,因为从编译器的角度来看,您可以将OutOfMemoryError.class放入temp,这会违反其类型。

修改

s3适用于您的原因是因为Class<? extends Exception>可分配给Class<? extends Throwable>

//this works
Class<? extends Exception> exceptionRef = Exception.class;
Class<? extends Throwable> throwableRef = exceptionRef;

因此extends关键字可让您将Set<Class<? extends Exception>>转换为Set<? extends Class<? extends Throwable>>

//this works too
Set<Class<? extends Exception>> exceptionSetRef = ...;
Set<? extends Class<? extends Throwable>> throwableSetRef = exceptionSetRef;

不幸的是,这可能不是您想要的,因为现在您无法将任何内容放入throwableSetRef

答案 1 :(得分:4)

我认为这是因为最接近的常见类型Exception和RuntimeException是Exception,而不是Throwable。调用createSet()的T被推断为Class<? extends Exception>,将Set<Class<? extends Exception>>分配给Set<Class<? extends Throwable>类型的变量

是非法的

这有效:

Set<Class<? extends Exception>> s1 = createSet( Exception.class, RuntimeException.class );

答案 2 :(得分:1)

为了使s1s2的问题更加明确,我们将Class<T>替换为List<T>

然后:

List<RuntimeException> runtimeExceptions = new ArrayList<RuntimeException>();

Set<List<RuntimeException>> listsOfRuntimeExceptions =
    new HashSet<Lis<RuntimeException>>();
listsOfRuntimeExceptions.add(runtimeExceptions);

Set<? extends List<? extends Throwable>> listsOfThrowables = 
    listsOfRuntimeExceptions; // This is legal 

Set<List<? extends Throwable>> s1 = listsOfThrowables; // Imagine that this is legal
s1.add(Arrays.asList(new Exception())); // Type safety of listsOfRuntimeExceptions is violated

Set<? extends List<Throwable>> s2 = listsOfThrowables; // Imagine that this is legal
s2.get(0).add(new Exception()); // Type safety of runtimeExceptions is violated

答案 3 :(得分:1)

我认为这里的问题来自编译器如何确定您拥有的varargs的“上限”(就类型而言)。如果我们检查http://download.oracle.com/javase/tutorial/java/generics/non-reifiable-varargs-type.html,我们可以看到,对于s1,您得到的上限是Class<? extends Exception>(并且会生成警告,告知它已生成)。然后是错误,因为最后您尝试将Set<Class<? extends Exception>>转换为Set<Class<? extends Throwable>>。如果您将s1重写为:

Set<Class<? extends Throwable>> s1 = createSet( Exception.class, RuntimeException.class, Throwable.class);

然后上限类型为Set<Class<? extends Throwable>>,再次生成警告但没有错误。