4

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 { }
Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
sHAILU
  • 165
  • 1
  • 10

6 Answers6

6

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.

Mike
  • 2,434
  • 1
  • 16
  • 19
  • (Redwood) t; can be fine but now (Redwood) new Tree() because the object going to case is not a reference, it is a Tree object. Why compiler can't decide it at compile time? – sHAILU Jan 06 '13 at 09:58
  • 4
    `new Tree()` returns a reference to a Tree. You never have objects in Java. Only references to objects. – JB Nizet Jan 06 '13 at 10:01
  • JB Nizet, If we think in that way then it can be passed. – sHAILU Jan 06 '13 at 10:03
  • 1
    It's not the compiler's job to examine your logic. The logic is bad, not the cast. – Mike Jan 06 '13 at 10:07
6

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

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
  • for eg: `Redwood r = (Redwood) new Tree();` not showing compiler error – Subin Sebastian Jan 06 '13 at 10:01
  • @Subin Because a Tree could be a Redwood. @Grijesh - I believe you mean `Blackwood b = new Blackwood() ;`, not `Blackwood b = new Redwood();` – Mike Jan 06 '13 at 10:02
  • @Mike but compiler knows `new Tree()` will give only Tree which cannot be qualified as a RedWood. My question is, it is failing in Runtime but why compiler cant pick it up as it clearly fails in all runtime situation. – Subin Sebastian Jan 06 '13 at 10:05
  • 1
    You could write a compiler to do that if you'd like, but it's not something that would be considered part of the compiler's duties. The compiler's job is to compile code. If the code is compile-able, it will compile. The code fits into the Java specification, and as such, will be compiled. It's like picking up your car from the mechanic and saying "Well, why didn't he fill up the gas tank? It's almost empty!". The answer is, he COULD have filled up the gas tank, but it's not his job. – Mike Jan 06 '13 at 10:11
  • @Mike Thanks Mike!.. to correct `Blackwood b = new Redwood();` – Grijesh Chauhan Jan 06 '13 at 10:15
  • I am sure java compiler does more than just compiling, this is not a real world use case for sure. But there are many compiler checks which doesnot allow logical errors. For eg: if you initialize a variable in if condition it warns , variable may never be initilaized. – Subin Sebastian Jan 06 '13 at 10:15
  • @Subin - Agreed. There are plenty of extra features that can be added, but at it's core a compiler compiles. The rest is gravy, and you don't always get all the gravy you want. – Mike Jan 06 '13 at 10:16
  • @Subin The compile-time rules are there to catch attempted casts in cases that are simply not possible. – Grijesh Chauhan Jan 06 '13 at 10:17
  • @GrijeshChauhan this is such a case. `cases that are simply not possible`. read the above comments – Subin Sebastian Jan 06 '13 at 10:18
  • @Subin possible if you want to cast redwood to balckwood! – Grijesh Chauhan Jan 06 '13 at 10:20
  • @Subin You're looking at the logic, not the syntax. The compiler does view this as `(Redwood) new Tree()`. It views this as `(Redwood) (refernce_to_Tree)`, which is valid syntax. – Mike Jan 06 '13 at 10:22
  • @Subin - as for `if you initialize a variable in if condition it warns , variable may never be initilaized.`, I believe that is specifically covered under 'Definite Assignment` in the Java specification and therefor must be examined by the compiler. – Mike Jan 06 '13 at 10:23
  • @GrijeshChauhan : we all know how it works . compiler checks the runtimetype of new Blackwood and see s that it is not castable to Redwood. but in case of Tree runtime type is castable to RedWood. Question is why cant compiler be a bit more intelligent and see that new Tree() will never be a Redwood. and answer is, it dont want to do that as trying to warn for that will make compiler bloated with unwanted code – Subin Sebastian Jan 06 '13 at 10:24
  • It COULD be more intelligent. But the purpose of the compiler is not to be intelligent. It is to compile code that conforms to the Java specification. That code conforms to the Java specification, and is therefor compiled. As I menioned before, it's like picking up your car from the mechanic and saying "Well, why didn't he fill up the gas tank? It's almost empty!". The answer is, he COULD have filled up the gas tank, but it's not his job. – Mike Jan 06 '13 at 10:26
  • @Mike yep :-) compiler has to check only whether the code is valid according to JLS. and thanks for the 'Definite Assignment' part. – Subin Sebastian Jan 06 '13 at 10:27
5

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.

ignis
  • 8,692
  • 2
  • 23
  • 20
  • agree. but is it not safe to assume runtime type of `new Tree()` is never Redwood and compiler give a warning. – Subin Sebastian Jan 06 '13 at 10:07
  • 1
    @Subin. The language could have a rule for that, but it does not. That's good, it would clutter the language for a pointless case that you will never find in the real world. – ignis Jan 06 '13 at 10:10
2

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!

Community
  • 1
  • 1
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
0

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.

Val
  • 1
  • 8
  • 40
  • 64
  • (Redwood) new Tree() can't be valid at compile time as well as at runtime because the object going to case is not a reference, it is a Tree object. Why compiler can't decide it at compile time? – sHAILU Jan 06 '13 at 10:01
  • A compiler doesn't execute the code. It just checks if it's valid. You are providing a Tree, which is a class that could possibly be a Redwood. It's not the compiler's job to see if your logic makes sense. You could also compile `int a = 1/0;` despite it being guaranteed to give you a `ArithmeticException`. – Mike Jan 06 '13 at 10:05
  • I show you the opposite. Please note that I assign Redwood to the Tree, not Tree to the Redwood. Secondly, your code fails not because there is a conflict between references and objects. Object = reference in Java. You must take some basic course. – Val Jan 06 '13 at 10:06
  • Object =/= reference. But this has nothing to do with the OP's problem. – ignis Jan 06 '13 at 10:07
0

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.

erencan
  • 3,725
  • 5
  • 32
  • 50