Question

Please check the below program.

I have doubt when compiler will issue casting exception at compiler level and when it will be at runtime?

Like in below program, expression

I assumed (Redwood) new Tree() should have failed at compiler time as Tree is not Redwood. But it is not failing in compile time, as expected it failed during runtime!!!

public class Redwood extends Tree {
     public static void main(String[] args) {
         new Redwood().go();
     }
     void go() {
         go2(new Tree(), new Redwood());
         go2((Redwood) new Tree(), new Redwood());
     }
     void go2(Tree t1, Redwood r1) {
         Redwood r2 = (Redwood)t1;
         Tree t2 = (Tree)r1;
     }
 }
 class Tree { }
Était-ce utile?

La solution 2

The compiler will just look at the compile-time type of the expression. It does not do assumptions on the runtime type of the expression. new Tree() has compile-time type Tree, so (Redwood)new Tree() is no different from (Redwood)myTreeVariable.

Autres conseils

A Tree is not necessarily a Redwood, but it could be, hence the lack of a compile-time error.

In this case it's pretty obvious it isn't, but compilers tend to complain about things that are definitely incorrect, rather than probably or possibly incorrect. In this case the logic is incorrect, not the code itself, and logic is your problem, not the compiler's.

Tree t = new Redwood();
Redwood r = (Redwood) t;

Perfect valid at both compile and run time.

I added one more subclass of in my explanation.

       Tree
      /    \
     /      \
    /        \ 
Redwood       Blackwood  

In this hierarchy:

Upcast: When we cast a reference along the class hierarchy in a direction from the sub classes towards the root. We need not use a cast operator in this case

Upcase Example:

A Redwood or Blackwood both are tree: So

Tree t1;
Tree t2;
Redwood r = new Redwood() ;
Blackwood  b = new Blackwood() ; 

t1 = r;    // Valid 
t2 = b;    // Valid   

Downcast: When we cast a reference along the class hierarchy in a direction from the root class towards the children or subclasses. We need explicitly type cast.

Downcast Example:

Redwood r = new Tree();  //compiler error, because Tree is not a Redwood 
Blackwood r = new Tree();  //compiler error, because Tree is not a Blackwood  

You need to explicitly type cast a Tree object if it really points to either Redwood or Blackwook object, other wise it error at runtime. e.g.

in this case:

Tree t1;
Tree t2;
Redwood r = new Redwood() ;
Blackwood  b = new Blackwood() ; 

t1 = r;    // Valid 
t2 = r;    // Valid     

Redwood r2 = (Redwood)t1; 
Blackwood  b2 = (Blackwood)t2  

[ANSWER]

Redwood r = (Redwood) new Tree(); why no compiler error?

its example of Downcast:

source Redwood r = (Redwood) new Tree(); fist create Tree object and typecast to Redwood.

you can think this as follows:

Tree t = new Tree();`
Redwood r = (Redwood)t;  

So its ok at compile-time,

[ANSWER]

Why Runtime error?

But really Redwood subclass can't point to Tree supper class object. so your code fails at runtime.

Tree t = new Tree();

t points to Tree() object not Redwood(). that's the reason of runtime error.

Compiler do not now what is value in t and syntactically every thing is write. But at run time due to Redwood r = (Redwood)t;, where t is a object of Tree class if became faulty.

[SUGGESTION:]

I would like to suggest you to use instanceof operator:

The casting/coercing operation is used to convert between types and the instanceof operator is used to check for type information at run time.*

Description:

The instanceof operator allows you determine the type of an object. It takes an object on the left side of the operator and a type on the right side of the operator and returns a boolean value indicating whether the object belongs to that type or not. This is most clearly demonstrated with an example:

if (t instanceof Redwood)
{
    Redwood r = (Redwood)t;
    // rest of your code
}

But I would also add: Casting from a base type to a derived type is a bad thing. Reference: Beware of instanceof operator

I have doubt when compiler will issue casting exception at compiler level and when it will be at runtime?

Strictly speaking, the compiler does not "issue casting exceptions". You will either get:

  • a compilation error, or
  • a runtime exception.

The reason that (Redwood) new Tree() gives a runtime exception and not compilation error is that that is what the JLS (section 5.5.1) says should happen.

Specifically, the JLS says this:

"Given a compile-time reference type S (source) and a compile-time reference type T (target), a casting conversion exists from S to T if no compile-time errors occur due to the following rules."

"If S is a class type" AND "If T is a class type, then either |S| <: |T|, or |T| <: |S|. Otherwise, a compile-time error occurs."

Where "|S| <: |T|" means type S is either type T or a subtype of T.

In this case S is Tree and T is Redwood, both are classes and Redwood is a subtype of Tree ... so there is no compilation error.

The fact that it is obvious that this is "wrong" is not relevant. The JLS says it is legal Java, and therefore that it should not give compilation errors. (A smart compiler may issue a compilation WARNING to the effect that the expression will always throw an exception ... but that's a different issue.)


The reasoning behind the JLS's rule is not spelled out in the spec, but I imagine it goes like this:

Compare these three fragments:

Redwood r = (Redwood) new Tree();

Tree t = new Tree();
Redwood r = (Redwood) t;

Tree t1 = new Tree();  Tree t2 = new Redwood();
Redwood r = (Redwood) (someCondition ? t1 : t2);

Tree t = gardenStore.purchaseTree();
Redwood r = (Redwood) t;

Suppose they defined the first fragment to be a compilation error:

  • What about the second one? Well that's easy to prove too.

  • What about the third one? That might be easy ... or it might be fiendishly difficult.

  • What about the fourth one? Now the legality of the fragment depends on the semantics of a method that we might not even have source code for!

The point is that once you start requiring the compiler to prove things about the dynamic value of expressions, you are on a slippery slope that leads to the Halting problem. And if you make the compilation error optional, then you get the horrible situation where one compiler can say that a program is valid, and another can say that it has errors!

The referenced Tree may be a Redwood beforehand that is why the code is compiled well.

     Tree t1 =  new Redwood(); 
     Redwood r1 = (Redwood)t1;
     Tree t2 = (Tree)r1;

You can see that the Tree() may be already a Redwood.

It is necessary to add that there is no need to downcast: Since Redwood is also a Tree, you can always assign it to a tree variable,

Redwood r;
Tree t = r; // no casting needed

You may always look for Substitution Principle in Java typesystem. I am sure thare are gigatonnes of materials.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top