It seems like I don't understand Casting.
Here my example:
Why in line 11 this is possible --> go2((Redwood) new Tree(), new Redwood()); ?
Isn't this a downcasting?
I understand that something like this is possible:
Tree tree = new Redwood();
Redwood redwood = (Redwood) tree;
But how do I have to read (Redwood) new Tree() ? I thought not every Tree has to be a Redwood, and thus this casting isn't possible, but it is. So I must have misunderstood something...
Enlighten me please
The compiler will only step in if the cast cannot possibly work based on the reference types. This generally happens when the types are in different branches of the inheritance tree. Try casting your newly created Tree object to a String, and the compiler will go "no, that cannot possibly work - you must have made a mistake".
Casting straight after creating the object is pointless - as you say, it shouldn't work. So avoid doing it. But there's a limit to how far the compiler will go in helping you not make mistakes.
Sam Samson wrote:Ok, so is it right that a cast is never possible on creating new objects? But why the compiler doesn't complain? I mean, what could happen between/before/during (?) the creating of the object that such a cast might ever be ok?
No, it's perfectly legal. The rules for casting references are thus:
1) At compile time, it is only legal to cast up or down a single branch of the type hierarchy. So if D extends C which extends B which extends A, then something that is declared to be of any one of those types can be cast to any other of those types, because as far as the compiler is concerned, the actual runtime object *could* be of the type you're casting to. Even if you can see by the code that it won't be, the compiler doesn't care. It doesn't execute the code to see what will happen.
If there's some other type E, which is not in the A, B, C, D "subtree", then it is *not* legal to cast between E and A, B, C, or D, in either direction. That is (B)E and (E)B are both illegal. This is an error at compile time, because the compiler knows that something declared to be a B can never be an E, and vice versa.
2) Even if the cast succeeds at compile time, you can get a ClassCastException at runtime if the object pointed to is not of the appropriate type
Sam Samson wrote:
The line Peach peach = (Peach) fruitLemon; throws a ClassCastException at runtime.
Right because the object that the fruitLemon points to is not a Peach, nor any subclass of Peach. As per my rule #2 above.
Is it true that the compiler looks only at the reference variable when casting, and not what object is really referenced?
Correct. As per my rule #1 above. Since fruitLemon is declared to be of type reference-to-Fruit, the compiler knows that it could point to a Peach object at runtime, and it trusts that you know what you're doing when you say, "I know that you only know this as a Fruit, but trust me, it will actually be a Peach."
If we wait until runtime to determine which class to instantiate, there's no way for the compiler to know what the object is, so it either has to allow that cast, meaning if we screw it up it's our own fault, or it has to disallow it, meaning a lot of code would get a lot harder to write. (And while there are cases where the compiler could theoretically tell what runtime object will be, the reasons why it doesn't do so boil down to, "It keeps the language rules and the compiler simpler."
And, after all, the whole point of casting is that it allows us to say, "I know better than the compiler," and then superseded some rules of the language's type system.