This question already has answers here:
Cast reference of known type to an interface outside of type's hierarchy
(6 answers)
Closed 8 years ago.
interface I{}
class A implements I{}
class B{}
First:
I[] arr = new A[10];
arr[0] = (I) new B(); // will produce ClassCastException at runtime
Second:
wherein if I use concrete class
I[] arr = new A[10];
arr[0] = (A) new B(); // will produce compile-time error
What's the difference if in my first example,(I) new B(), the java compiler should produce compile-error as well?
Isn't it that the java compiler should be able to distinguish that it is also an "inconvertible type"? Especially when the new operator comes immediately?
Is there any instance/chance that it will be possible that creating that new instance of B could produce a
convertible type of type I?
I know at some point, that the Java compiler should not immediately say that it is a compiler error, like when you do this:
I i = (I) getContent(); // wherein getContent() will return a type of Object
Edit:
Let me clarify this question why it is not a possible duplicate of this: Cast reference of known type to an interface outside of type's hierarchy
The intention of this question is not because I am not aware of what will be the result of or what is something wrong with something, etc.
I just want to know a "more detailed explanation in a technical way" of why does the JVM behave that way or why does Java came up with that kind of decision of not making that kind of scenario a compile-time error.
As what we all know, it is always better to find "problematic code" at compile-time rather than at run-time.
Another thing, the answer I am looking for was found here on this thread not on those "duplicates?".
The rules for what casts are compile-time legal only take into account the static types.
When the Java compiler analyzes the expression (I) new B(), it sees that the static type of the expression new B() is B. We can tell that new B() can't possibly be an instance of I, but the compile-time analysis rules can't tell that the object isn't actually an instance of a subclass of B that implements I.
Thus, the compiler has to let it through. Depending on how sophisticated the compiler is, it might detect the oddity and issue some sort of warning, but in the same way 1/0 isn't a compile-time error, this can't be a compile-time error.
The difference in this situation is obviously that I is an interface, which could be implemented. This means, that even though B has nothing to do with I, there could be a subclass of B, that implements the interface I. Will illustrate this with an example:
interface I{}
class A implements I{}
class B{}
class C extends B implements I{}
I[] arr = new A[10]; // valid, cause A implements I
B b = new C(); // Valid because C is a subclass of B
arr[0] = (I) b; // This won't produce ClassCastException at runtime, because b
// contains an object at runtime, which implements I
arr[0] = (I) new B(); // This will compile but will result in a ClassCastException
// at runtime, cause B does not implement I
It's important to distinguish the difference between static and dynamic types. In this case the static type of the variable b is B, but it has the dynamic type (the runtime type) C, where new B() does also have the static type B, but the dynamic type is also B. And as in some cases a cast from B to I won't result in exceptions (as in this scenario), the compiler allows such casts but only to an interface type.
Now take a look at the following scenario:
I[] arr = new A[10];
B b = new C(); // Valid because C is a subclass of B
A a1 = (A) b; // compile time error
A a2 = (A) new B(); // compile time error
Is it possible that a subclass of B will ever extend A and B at the same time? The answer is NO, cause you are limited to only one super class to extend in Java (where in some other OO languages this is not the case), therefore the compiler forbids it, cause there are no possible scenarios, where this will work.
You can cast any object to the desired var, only if you cast it to the var type:
interface I{}
class A implements I{}
class B{}
I var = (I) object; // This is always possible in compile-time, no matter the object type, because object is casted to the var type, I
I var = (A) object; // Not possible in compile-time because of the difference of types
The runtime exception comes when the object cannot be casted, but you can't know it until runtime.
A object = new A();
I var = (I) object;
B anotherObject = new B();
var = (I) anotherObject;
Both of the above will work in compile-time, but only the first one will do it in runtime, because of the implementation of the I interface.
Class inheritance is single inheritance, no future descendant can introduce a new base class, e.g. in your example, no descendent of B can ever be cast to A. But it can introduce a new interface, i.e. a descendent of B could support I.
It's a failing of the compiler that it can't solve your simple case, but it's not a case you would ever see in the wild. Create and cast in one line that is.
Example of why the compiler can't detect this in a more complex case using your classes
void method(B b){
I i = (I) b;
}
class C extends B implements I{} // a descendent of B that introduces support for I
method(new A()); //still compile time error
method(new B()); //runtime exception
method(new C()); //works
I tried four cases:
Casting a class to another class.
Casting a class to an interface.
Casting an interface to a class.
Casting an interface to another interface.
Compile-time error only happens in the first case.
static interface I {}
static interface J {}
static class A {}
static class B {}
Object o = (B) new A(); // compile-time error
Object o = (I) new A(); // runtime error
Object o = (B) ((I) new A()); // runtime error
Object o = (J) ((I) new A()); // runtime error
My guess is that this happens because determining whether the cast will succeed or not relatively easier in the first case, compared to the other three cases. The main reason is that a class can only extend one class, which allows the compiler to reason whether the cast will succeed or not. See these examples:
Suppose, in addition to the above classes and interfaces, I add new class:
static class C extends B implements I, J {}
Example of code that casts a class to an interface (last line):
C c = new C();
B b = (B) c;
I i = (I) b; // This is ok.
Example of code that casts an interface to a class (last line):
C c = new C();
I i = (I) c;
B b = (B) i; // This is ok.
Example of code that casts an interface to another interface (last line):
C c = new C();
I i = (I) c;
J j = (J) i; // This is ok.
Related
I am preparing for java certification and unable to find any concept or logic behind this.
Can anyone help me understanding the concept of multiple typecasting. I can understand the one level of type casting but I am not getting any information for these conversions.
Here is the sample I am trying to understand.
interface I{
}
class A implements I{
}
class B extends A {
}
class C extends B{
}
A a = new A();
B b = new B();
Now option 1 don't have any error at compile time or runtime, while 2nd option is having error. I run it in eclipse but unable to understand the logic behind this.
1. a = (B)(I)b;
2. b = (B)(I)a;
Your question is a bit vague but I'll see if I can help.
So you have your interface I and classes A, B and C.
You can now do things like these:
I i1 = new A();
I i2 = new B();
I i3 = new C();
This is possible because, by inheritance, all the classes A, B and C implement I.
You can also do
A a1 = new B();
A a2 = new C();
for almost the same reason (difference being they are extending A rather than implementing an interface).
You probably guessed by now that you can also do
B b1 = new C();
What you can't do is something like
B b = new A(); // Type error
C c = new B(); // Type error
B b2 = (B)new A(); // ClassCastException
This is not possible since the type A does not extend from B.
In general you don't need to explicitly type cast in these cases.
I i = (I)new A(); // <- not necessary
It can be necessary in the opposite direction however:
I i = new B();
B b = (B)i; // <- cast is needed.
This works if and only if i is actually of type B (or C). If the object i is not of type B or a type that extends from B you will get ClassCastException.
Now for your question
Both your two options are a bit strange. Double casting is not something you would normally do. the cast to I is unnecessary.
With that said, 1) will work and 2) will not. Your object b cannot be assigned a because A does not extend from B.
Edit
To be clear, when you instantiate an object, for example
A a = new A();
The actual object (I'm going to call it o) is an instance of class A. You also have an object reference called 'a' which is typed as A which refers to the object o.
When you assign other object references, the type of the actual object doesn't change. When assigning a reference, the type of the object must be the same type as the reference or of a type that inherits from the referred type.
For example, you can assign a reference of type I:
I i = a;
This assigns the reference i to the same object that a refers to which is the object o. Note that o is still of type A.
System.out.println(i instanceof A);
will print
true
Suppose I have three classes A, B and C.
C extends B and B extends A.
A implements an interface InterfaceA
B implements interfaces InterfaceB1 and InterfaceB2.
What is the best way to find all possible combinations of downcasting?
What I mean is?
Suppose we have:
B b = new B();
InterfaceA i = (InterfaceA)(A)(B) b.
How can we easily know if this compiles and if it will cause a classcastException without an IDE?
I know how objects and references work and have a decent understanding of polymorphism in Java.
I started to draw a sketch of the class and interface structure.
EDIT: I know my example is not correct but I always struggle when interfaces join the story.
Let's deal with "whether or not it compiles" first.
The key to determining this is to look at the compile time type, i.e. the declared type of the variables.
Generally,
Casting from a compile time interface type to an interface type always compiles.
Casting from a compile time interface type to a class only compiles if any of the following is true:
the class is non-final
the class implements the interface
Casting from a compile time class type to an interface type only compiles if any of the following is true:
the class is non-final
the class implements the interface
Casting from a compile time class type to another class type only compiles if one of them inherits from the other class.
To determine whether the cast succeeds at runtime, the key is to look at the runtime type of the object stored in the variable. For example:
A a = new B();
The compile time type of a is A, but the runtime type is B.
Generally, casting from runtime class type T to a class or interface type U only succeeds if T inherits from U or implements U.
For future readers,
If instead you want to know how to check if a cast will succeed using code, you should refer to this question.
First of all, in your scenario the following is not a downcasting at all because in described hierarchy B is B, B is A and B is InterfaceA. So any of these casts can be omitted.
InterfaceA i = (InterfaceA)(A)(B) b;
Then, answering your question. Downcasting will be compilable then and only then when it is possible to be successful in runtime. Let's introduce additional class D, that extends class A and implements InterfaceB1.
A a = new A();
InterfaceB1 b1 = (InterfaceB1) a; // compilable as A reference can contain object of class B that implements InterfaceB1
InterfaceB2 b2 = (InterfaceB2) a; // compilable as A reference can contain object of class B that implements InterfaceB2
b1 = (InterfaceB1) b2; // compilable as InterfaceB2 reference can contain object of class B that implements both InterfaceB1, InterfaceB2
B b = (B) a; // compilable as A reference can contain object of class B
C c = (C) a; // compilable as A reference can contain object of class C
D d = (D) a; // compilable as A reference can contain object of class D
d = (D) b1; // compilable as InterfaceB1 reference can contain object of class D in runtime
b = (B) d; // not compilable since D reference can NEVER contain object of class B in runtime
c = (C) d; // not compilable since D reference can NEVER contain object of class D in runtime
As for causing ClassCastException it is always about what actually is contained in your object reference.
A a1 = new A();
A a2 = new B();
InterfaceB1 b1 = (InterfaceB1) a1; // compiles but causes ClassCastException as A cannot be cast to InterfaceB1
InterfaceB1 b2 = (InterfaceB1) a2; // compiles and runs just normally as B can be cast to InterfaceB1
I thought you can't cast Parent to Child, and can only cast Child to Parent. Or at least if you did, you wouldn't be allowed to access the child's functions.
Then I see that Java will allow me to explicitly cast parent to child:
a=(B)a;
but will not allow
bArray[0]=a
or
a.testB();
Yet this is ok:
bArray[1]=(B)a;
and so is this:
bArray[1].testB();
Can someone please explain what's going on here!? How can a parent class be given functions of a child class? And why does the first attempt at placing a into the array not work, but the second attempt does? Shouldn't it be the same?
class A {
public void testA() {
}
}
class B extends A {
public void testB() {
}
}
public class polymorphicObjects {
public static void main(String[] args) {
B bArray[] = new B[5];
A a = new A();
A ab = new B();
a = (B)a; //didnt think you could do this
bArray[0] = a; //but if I can the why doesnt this work?
a.testB(); //or this
bArray[1] =(B)a; //but this does work
bArray[1].testB(); //and so does this!?
}
}
Does running bArray[1].testB(); actually work? That should throw a ClassCastException at least. Adding a print statement inside the testB() method should help with that.
So in this, you're saying a = (B)a; which is very explicitly casting a to type B, but it doesn't actually mean anything.
With that in mind, this doesn't work in the first line, bArray[0] = a; because it's no longer explicitly cast, so the compiler will complain that you're giving a type that doesn't match.
In the subsequent line bArray[1] = (B)a; you are explicitly telling the compiler that a is of type B, so it won't bark at you... although the call to the method testB() should fail at runtime.
In Java, casting has a very specific meaning (different from the C language, e.g.). You have to distinguish what class your object actually has, and what the compiler thinks about the class of some expression.
Casting doesn't change the actual object class, but only what the compiler thinks about the class of the expression.
Let's walk through your code:
new A() will always give you an object of class A, and the compiler knows that.
You store that into a reference variable a, declared to be of class A, so from the declaration (not the new A() expression) the compiler knows that variable a has class A (allowing subclasses as well).
With the cast expression (B) a you don't change anything about the object that's stored in a, just assert to the compiler that you, as the developer, know that the object stored in a is of type B (which is not true, in your case, it's an A, not a B). The compiler isn't intelligent enough to see that, so he treats the expression (B) a as valid and of class B. He allows you to cast from A to B because B is a subclass of A, so a variable of class A might perfectly well contain an object of class B (e.g. A a = new B(); would be perfectly OK). At runtime, the JVM will check for the actual object class and raise a ClassCastException as it finds out that it's an A not coming from subclass B.
With a = (B) a; you store your same object back into the same variable where it came from.
You ask about bArray[0] = a; This doesn't compile because for the compiler, a contains an object of class A, and the array needs a B. bArray[1] = (B) a; will compile, but throw a ClassCastException when run as long as the variable still contains the same object of class A.
And bArray[1].testB(); compiles because the compiler knows that in the bArray all elements are of class B, and class B has this method. As trying to store an object which isn't of class B into bArray won't work (either at compile-time bArray[0] = a; or at run-time bArray[1] = (B) a;), calling the testB() method is safe.
I have read that the entire point of Generics is that the add stability to our code by making more of our bugs (essentially that ones which occur when a variable is assigned a value whose type is not compatible with the type of the variable) detectable at compile time.
Following is a non generic class in which I get a RunTimeException, ClassCastException occures, at the statement B bForStoringReturnedAOne = (B) box.aMethod(c);. I was expecting that if I make this code Generic, this ClassCastException will not occur, as the use of Generics will somehow make the bug causing the exception, DETECTABLE AT COMPILE TIME.
So I posted a generic version of this code as well. The problem is that no bug is detected at COMPILE TIME, and I get the same ClassCastException at the same statement. So the question is that what is the difference? What have generics helped with? What is the point of existence of generics? Even after using generics, the bug/exception is still not detected at compile time.
NON-generic version:
public class SomeClass {
private class A {}
private class B extends A {}
private class C extends A {}
private class Box {
private A aMethod(A a) {
return a;
}
}
public static void main(String[] args) {
SomeClass someClass = new SomeClass();
B b = someClass.new B();
C c = someClass.new C();
Box box = someClass.new Box();
B bForStoringReturnedA = (B) box.aMethod(b);
B bForStoringReturnedAOne = (B) box.aMethod(c);//*****ClassCastException
}
}
Generic version:
public class AnotherClass {
private class A {}
private class B extends A {}
private class C extends A {}
private class Box<T> {
private T aMethod(T t) {
return t;
}
}
public static void main(String[] args) {
AnotherClass someClass = new AnotherClass();
B b = someClass.new B();
C c = someClass.new C();
Box<A> box = someClass.new Box<>();
B bForStoringReturnedA = (B) box.aMethod(b);
B bForStoringReturnedAOne = (B) box.aMethod(c);//*****ClassCastException
}
}
Generics are perfectly doing their job in the example you gave.
You make a Box<A>, which has aMethod that takes in A and returns A (after type inference).
You pass it a B, and the method returns it as an A. You then cast it to a B, which works since the object actually is a B.
You then pass it a C and it is also returned as an A. You then cast it to a B which throws an exception since the object is not actually a B.
This is basically the same as doing:
Box<A> box = someClass.new Box<>();
A a1 = box.aMethod(b);
A a2 = box.aMethod(c);
B b1 = (B) a1;
B b2 = (B) a2;
I don't see how you expected generics to help you there.
If you however made a Box<B>:
Box<B> box = someClass.new Box<>();
B b1 = box.aMethod(b); // OK, + no need to cast
B b2 = box.aMethod(c); // Compile time error
error: method aMethod in class Box<T> cannot be applied to given types;
B b2 = box.aMethod(c); // Compile time error
^
required: B
found: C
reason: argument mismatch; C cannot be converted to B
where T is a type-variable:
T extends Object declared in class Box
1 error
The compiler correctly guarantees type safety, by giving an error.
I was expecting that if I make this code Generic, this ClassCastException will not occur, as the use of Generics will somehow make the bug causing the exception, DETECTABLE AT COMPILE TIME.
Nope. An explicit type cast isn't a compile-time operation, it's a run-time operation. Any time you cast a type, you're basically telling the compiler that you know more than it does about what the run-time type will be and that the compiler should trust you on the matter.
In both the presented cases, the information you knew that the compiler didn't know turned out to be incorrect. (Intentionally, of course, for the sake of what you were illustrating.) Hence the exception.
Generics carry just as much compile-time type safety as any other classes. There's no magic behind them, they can't detect future run-time errors before they happen.
What they do is provide a kind of "template" for a variety of types. A Box<A> is an entirely different type from a Box<B>, and carries all the compile-type type safety for itself that Java provides. Box<> by itself is just sort of a template for those types, but at compile time the specific type must still be known.
Basically, the return value of a Box<A>.aMethod() is A. It's not dynamic, it's not changeable, it's A. Just like in the non-generic version.
What generics give you is the ability to write these re-usable "template" types which can be combined with many other types to form the actual compile-time resulting type.
I have a broad question regarding Java casting via classes. Let's say I create 4 classes (well 3 classes and 1 interface), Interface A is the super interface I guess you could say and Class B implements A (meaning that it is the subclass of the interface A) and C extends B and then D extends C.
Let's say that I have a driver class in which I initialize the following like below:
A myA;
B myB = new B();
C myC = new C();
D myD = new D();
//I want to cast now!
myB = (B) myD;
myC = (D) myA;
myD = (C) myB;
When are these fabricated objects actually compilable? I'm having a bit of a difficult time understanding the rules between casting. I do kind of understand Down-casting and how it's not permitted, but I guess class casting is still a concept that sort of confuses me.
The thumb rule is that if an object B is of type A, then it can be casted to A. In you example B implements A so B is of type A. You can cast any B object to A. Since C extends B, C is of type B as well as of type A. So C objects can be cast to A or B.
Wanted to add as comment, but coz of limitation, had to add it as answer:
1) Rule is Child can inherit what father/parent has, but not reverse.
2) Child can be stored as parent, but not reverse.
That makes myD =(C)myAB; uncompilable as myD extends C (which extends B-->A)
So when you create
B myB = new B();
If I try to explain in non technical terms, Then myB knows everything about B and A, but it does not know what is below. It can see and identify itself with anything above it in following hierarchy:
A
B
C
D
So lowest one D can be casted to anything that lies above it.
1) myB = (B) myD;
With rule state above, D is below B and hence can be assigned to B.
2) myC = (D) myA;
Here you have casted interface to D, and hence in compile time, D can be assigned to top level C.
3) Here myB (which in that statement is typecasted to C in compile time) can't be assigned to D at compile time (although it is instance of D which is lower in hierarchy) but when you are compiling, you don't have runtime instance available. So below will fail:
myD = (C) myB;
I assume I have not confused you further here.