16

Studying for my OCA Java SE 7 Programmer I exam, so newbie question. I have an example question I do not understand. The following code compiles, but gives a ClassCastException at runtime:

interface Roamable {
}

class Phone {
}

public class Tablet extends Phone implements Roamable {
    public static void main(String... args) {
        Roamable var = (Roamable) new Phone();
    }
}

When I change Roamable var = (Roamable) new Phone(); into Roamable var = (Roamable) new String(); I get a compilation error right away.

Two questions:

  1. Why does the code above compile at all? Phone seems unrelated to Roamable to me?
  2. Why does the code compile with new Phone(), but doesn't it compile with new String()?
luukburger
  • 637
  • 6
  • 14

7 Answers7

14

Why does the code above compile at all? Phone seems unrelated to Roamable to me?

yes because Roamable is an interface it might cause a Run-time exception but not compile time exception, because even if Phone doesn't implement Roamable, a subclass of Phone might, hence the compiler has no way to know it but at the Run time.

It is already defined in java language specification. Check out my answer here.

Why does the code compile with new Phone(), but doesn't it compile with new String()?

Because class String is declared as public final class in java.lang package. As specified in jls 8.1.1.2 final class section: a class declared as final can't be extended and hence it won't have any subclass. So, the compiler already knows that String can't be extended: hence no subclass's existence is possible to implement interface Roamable

Edit: (With response to your below comment)

Let us assume that B is a subclass of A which implements an interface T.

Now an statement :

   T t = (T)new A();

is essentially same as:

   A aObj = new A() ;
   T t = (T)aObj ; // a run-time exception happen

before running into conclusion, let us do the same thing with an object of B:

   A aObj = new B();
   T t = (T)aObj; // no exception happen.

so, the real reason with super class and sub class here is the reference. The aObj class in this second code example is also an instance of class A but it is also an instance of class B which has implemented T.

Community
  • 1
  • 1
Sage
  • 15,290
  • 3
  • 33
  • 38
  • But the code in my example says to make a new `Phone`, not an object of some other class that might extend `Phone` and implement `Roamable`. I don't see why the compiler cares about a possibly existing subclass here? – luukburger Nov 11 '13 at 18:01
  • @luukburger, updated the answer. Check with the example provided. Assume class `A` as your `Phone` class – Sage Nov 11 '13 at 18:47
  • Hmmm... Thanks for your help. So the compiler says: `new Phone()` will hand me _an object of type `Phone`_. Maybe really a `Phone`, maybe a `Phone2` that implements Roamable. It doesn't see that no `Phone2` class exists thus that that assumption is wrong...? – luukburger Nov 11 '13 at 19:34
  • The existence of class `Phone2` will be known at none but run-time. :) Don't forget to check this site's [about page](http://stackoverflow.com/about). Welcome to stack overflow – Sage Nov 11 '13 at 19:40
4

String is final so there is no way it can be cast to a Roamable.

JamesB
  • 7,774
  • 2
  • 22
  • 21
  • 1
    So I could cast an object of any non-final class into a Roamable, as far as the compiler is concerned? – luukburger Nov 10 '13 at 21:34
  • 1
    @luukburger Yes, because the compiler does not know if a subclass of the casting type implements Roamable. If the class is final, no such subclass can exist. – JamesB Nov 10 '13 at 21:54
  • Also if you try casting to class (assume Roamable is class) will results in “cannot cast” compilation error – Kanagavelu Sugumar May 12 '16 at 14:34
  • Since java supports single inheritance for class which can be identified at compile time, but interface can be implemented at any sub class level (multiple inheritance) compiler cant identify. – Kanagavelu Sugumar May 12 '16 at 14:45
1

The code compiles because the compiler lets you cast to anything. It is an explicit action by the programmer, and the compiler assumes you have assessed the risks and taken the appropriate precautions.

String is not an instance of Roamable, so you cannot assign an instance of String to a Roamable reference. And that can be determined at compile time, so it fails.

Vidya
  • 29,932
  • 7
  • 42
  • 70
  • But `Phone` is also not an instance of `Roamable`? – luukburger Nov 10 '13 at 21:31
  • Not as you have it. To do that, you need `class Phone implements Roamable`. And then `Tablet` could just extend `Phone`. – Vidya Nov 10 '13 at 21:32
  • Thanks, but what I meant was: both String and Phone are (in the code above) not an instance of Roamable, but still `new String()` gives a compilation error and `new Phone()` doesn't... – luukburger Nov 10 '13 at 21:48
  • @luukburger, check out my answer. I would write here but it will rather take space – Sage Nov 10 '13 at 21:50
1

new Phone() evals into class Phone, which could be an extension of Phone that implements Roamable (as far as the compiler is concerned).

If you made Phone a final class (like String), you will get the compiler error.

Glenn Lane
  • 3,892
  • 17
  • 31
  • In particular, `Tablet` is an existing subclass of `Phone` that implements `Roamable.` If Phone was `final`, it'd be a trick to get the class `Tablet` to compile at all :) – jgriego Nov 10 '13 at 21:22
  • Sorry, didn't notice Tablet was extending Phone... not much point since it's never instantiated and just used for the main entry point – Glenn Lane Nov 10 '13 at 21:27
0

A good question. new Phone() is definitely not any subclass of Phone. However that information is lost, javac sees a Phone type there, not the exact Phone type.

Relevant spec: http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.5.1

If S is a final class (§8.1.1), then S must implement T,

The spec can be amended with

If the expression is a Class Instance Creation Expression (§15.9), the type of the expression must be a subtype of T.

ZhongYu
  • 19,446
  • 5
  • 33
  • 61
0

First read about Explicit and Implicit type casting by java.

From that user is responsible for explicit casting when narrowing down the object relationship to say that user knows and fine with they loose some precision because of this. However still compiler can detect some explicit wrong casting and throw CE: Type mismatch error at some point. Beyond that point it is up to runtime ClassCastException to deal with it.

Compiler can detect the following cases of explicit casting.

class A {}
class B {}

A a = (A) new B(); //CE: Type mismatch: cannot convert from B to A
B b = (B) new A(); //compilation error (CE)

interface I {}
final class A {}
I i = (I) new A();  //compilation error

Compiler can not detect the following cases of explicit casting.

class A {}
class B extends A {}
A a = (A) new B(); //Fine
B b = (B) new A(); //Runtime error; because compile time;
                   //compiler wont be able to tell the reference is instance of A or B.
                   //means this is something like below. <BR>
B b = (B) (A) new A();

Any Object can be caste to any interface without compilation error.

interface I {}
class A {}
class B extends A implements I{}

I i = (I) new A();  //Runtime error
I i = (I) new B();  //Fine

Why does this compile?

Reference of interfaces can castes to any object without compilation error.

interface I {}
class A implements I {}
class B {}

B b = (B) getI();     //Runtime error
OR 
B b = (B)(I)new A();  //Runtime error

public I getI() {
 return new A();
}
Community
  • 1
  • 1
Kanagavelu Sugumar
  • 18,766
  • 20
  • 94
  • 101
0

I think no need too many words. Simply kindly consider the following example by keeping in mind that a superior reference can be a pointer to its one of subclasses.

class Test {
   public static void main(String[] args) {
      
     class x {}
     interface z {}
     class y extends x implements z {}

     x xx = new x();
     // var q = (z)xx; // class cast exception
     xx = (new y()); // xx is kinda superior ref-type of "class y" right? It can point it.
     var qq = (z)xx;
    }
}

If we restrain it, how can the reference refer to the object after the correct casting? (at the last line)