问题
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:
- Why does the code above compile at all? Phone seems unrelated to Roamable to me?
- Why does the code compile with
new Phone()
, but doesn't it compile withnew String()
?
回答1:
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
.
回答2:
String is final so there is no way it can be cast to a Roamable.
回答3:
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.
回答4:
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.
回答5:
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.
回答6:
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();
}
来源:https://stackoverflow.com/questions/19895304/classcastexception-vs-cannot-cast-compilation-error