Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Set<? extends Class<? extends Throwable>>" really?

I have been using a little generic method to create sets from vararg of elements, eg

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

Recently, however, I came across a situation where the compiler did not do what I expected it to do. Of the following createSet() uses only s3 works.

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 );

Can anyone give a clear explanation of why s3 works and what might be wrong with my thinking regards s1 -- which was my initial coding? Thanks.

like image 209
Andrew Gilmartin Avatar asked Nov 02 '11 15:11

Andrew Gilmartin


3 Answers

The problem is simply with the inference logic. s1 works just fine (well, except for vararg warnings) if you explicitly type the method call:

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

But by default the return type given your parameters is a Set<Class<? extends Exception>> (I suppose because it's the most specific possibility). You just need to give the compiler a hint here, since without it, it is essentially trying to do this:

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

Which is not allowed since from the compiler's point of view, you could then put a OutOfMemoryError.class into temp which would violate its type.

Edit

The reason s3 works for you is because a Class<? extends Exception> is assignable to Class<? extends Throwable>:

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

and so the extends keyword affords you the ability to convert from a Set<Class<? extends Exception>> to a Set<? extends Class<? extends Throwable>>:

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

Unfortunately, that's probably not what you want since now you can't put anything into throwableSetRef.

like image 187
Mark Peters Avatar answered Oct 14 '22 00:10

Mark Peters


I think it's because closest common type of Exception and RuntimeException is an Exception, not a Throwable. T in invokation of createSet() is inferred to be Class<? extends Exception>, and it's illegal to assign Set<Class<? extends Exception>> to a variable of type Set<Class<? extends Throwable>

This works:

Set<Class<? extends Exception>> s1 = createSet( Exception.class, RuntimeException.class );
like image 40
socha23 Avatar answered Oct 14 '22 00:10

socha23


To make possible problems of s1 and s2 more clear, let's replace Class<T> with List<T>.

Then:

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
like image 28
axtavt Avatar answered Oct 13 '22 22:10

axtavt