views:

89

answers:

3

Why does the following code compile?
Why is it allowed to cast a generic list to its type parameter if the parameter is an interface but not a super-interface of the generic?

What does this mean?

//Connection can be substituted by any interface
List<Connection> list = null;
Connection c = (Connection) list; 
A: 

It compiles but it will fail at execution time.

Static casting is one way for a developer to tell the compiler I know what I am doing here - I will handle the typing of these variables myself. The compiler will not stop you from doing something that will throw an exception at execution time.

Andrew Hare
But it is clear at compile time that the cast will fail at runtime, isn't it? The compiler also stops me when trying to do String bla = (String) BigDecimal.ZERO; so why not here?
MRalwasser
More exactly it will throw a java.lang.ClassCastException.
Dave
-1: Actually, it will *not* fail at runtime because null is compatible with all types. And the compile *will* reject casts that are defeinitely impossible.
Michael Borgwardt
Static error checking does not aim to be perfect, just catch as many errors as it can. You can find 100s of ways to trick the compiler in to compiling bad code... most of us don't want a compiler that acts like a nanny state.
bwawok
@MRalwasser: it is NOT clear it will fail at runtime at compile time. `List` is an interface, and whatever sub-type of `Connection` is stored in the variable `c`, it may implement List. See @Michael Borgwardt's answer.
Tom Tresansky
+12  A: 

That has nothing to do with the type parameter. This works as well:

List<String> list = null;
Connection c = (Connection) list; 

It's possible because List is an interface type. It would be possible for the list reference to hold an object that both implements the List interface and is also a Connection (whatever that is, class or interface), and for which the cast therefore works.

Thus, since the cast could work, the compiler allows it. It will only reject casts that are theoretically impossible, i.e. which involve concrete types in separate inheritance hierarchies:

    JComponent c = null;
    ArrayList l = (ArrayList) c;

You can look up the exact rules for what types of casts are legal at compile time in the Java Language Specification - it's about 30 lines of dense language lawyering.

Michael Borgwardt
+1 - But ... *"It will only reject casts that are theoretically impossible ..."* based solely on the declared types. For instance if list was initialized to a final `List` class that did not implement or extend `Connection`, the compiler *still* has to allow this.
Stephen C
A: 

Same thing, like with the other typecasting. You are allowed to cast almost any Object to any other. Compilers do not allow the most idiotic typecasting (like List to String), but still - it's up to you to watch the typecasting. Generally, use

if(a instanceof bClass){
    ((bClass)a).doSomething(...);
}
Frozen Spider
What exactly makes some typecasting idiotic? That's the question?
Ishtar
@Ishtar: in the case of List to String, it can't work because String does not implement List and is final. The JLS specifies this kind of thing (looking it up now..)
Michael Borgwardt
@Micheal - Yes, that's what I figured too. I just meant with the comment, that this answer isn't really answering the question: `Why is it allowed to cast a generic list to its type parameter if the parameter is an interface but not a super-interface of the generic?`
Ishtar
@Ishtar Typecastings, that are obviously impossible. You cannot typecast to the final class, for example (if it's not implementing the same interface), as it is clear that final class do not have subclasses, which can be casted to their parent class.
Frozen Spider