我一直在使用一个小的泛型方法来从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 - 这是我的初始编码?感谢。
答案 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)
为了使s1
和s2
的问题更加明确,我们将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>>
,再次生成警告但没有错误。