When can a certain object be cast into another object? Does the casted object have to be a subtype of the other object? I'm trying to figure out the rules...
Edit: I realized that I didn't explain my issue at all: basically I am casting an object to an interface type. However, at run-time, I get a java.lang.ClassCastException. What needs to happen with my object so that I can cast it to this interface? Does it have to implement it?
Thanks
In Java there are two types of reference variable casting:
Downcasting: If you have a reference
variable that refers to a subtype
object, you can assign it to a
reference variable of the subtype.
You must make an explicit cast to do
this, and the result is that you can
access the subtype's members with
this new reference variable.
Upcasting: You can assign a reference
variable to a supertype reference
variable explicitly or implicitly.
This is an inherently safe operation
because the assignment restricts the
access capabilities of the new
variable.
Yes, you need to implement the interface directly or indirectly to enable assigning your class object reference to the interface type.
Suppose we want to cast d object to A,
A a = (C)d;
So internally 3 rules have been checked by Compiler and JVM.
The compiler is checking first 2 rules at Compile time and JVM will check last one rule at Runtime.
Rule 1 (Compile time checking):
Type of 'd' and C must have some relation (child to parent or parent
to child or same time).If there is no relationship then we will get a
compile error(inconvertible types).
Rule 2 (Compile time checking):
'C' must be either same type or derived type(subclass) of 'A'
otherwise we will get a compile error(incompatible types).
Rule 3 (Runtime Exception):
Runtime object type of 'd' must be same or derived a type of 'C'
otherwise we will get a runtime exception (ClassCastException
Exception).
Find following examples to get more idea,
String s = new String("hello"); StringBuffer sb = (StringBuffer)s; // Compile error : Invertible types because there is no relationship between.
Object o = new String("hello"); StringBuffer sb = (String)o; // Compile error : Incompatible types because String is not child class of StringBuffer.
Object o = new String("hello"); StringBuffer sb = (StringBuffer)o; // Runtime Exception : ClassCastException because 'o' is string type and trying to cast into StingBuffer and there is no relationship between String and StringBuffer.
There's an intuitive way of thinking about this - you're not changing an object with a cast, you're only doing something that would already be permitted if the type was known - inotherwords, you can only cast to a type that your object already is. So just look "up" the object chain to see what kinds apply to your object.
So you can cast to an interface only if it's defined somewhere higher up in the chain (e.g. if your classes parent implements it, etc. etc). It has to be explicit - from your question it sounds like you may be thinking that if you implement method "void foo()" then you should be able to cast to an interface that defines the method "void foo()" - this is sometimes described as "duck typing" (if it quacks like a duck, it's a duck) but is not how java works.
This will work:
class Foo implements Runnable {
public void run() {}
}
Foo foo = new Foo();
System.out.println((Runnable) foo);
But this will not:
class Bar {
public void run() {}
}
Bar bar = new Bar();
System.out.println((Runnable) bar);
Because although Bar has a run() method that could implement Runnable.run(), Bar is not declared to implement Runnable so it cannot be cast to Runnable.
Java requires that you declare implemented interfaces by name. It does not have duck typing, unlike some other languages such as Python and Go
You can cast if the runtime type of an object is a subtype of what you're trying to cast it into.
EDIT:
Yes, the object that you're trying to cast will need to implement the interface in order for you to cast it successfully.
If:
interface MyInterface{}
class MyClass implements MyInterface{}
Then
MyClass m = new MyClass();
MyInterface i = (MyInterface)m;
is possible.
Related
The Problem
Consider the code below. B inherits from A, and none of them inherit from String.
I'd like to know the answers to the following questions:
why does the first first cast, B b1 = (B) a1;, produce a runtime error?
why does the first second cast, String b1 = (String) a1;, produce a compilation error?
Why is there a difference? Why doesn't the compiler see the problem in the first case?
The Code
public class Main {
public static void main(String[] args) {
A a1 = new A();
B b1 = (B) a1;
String b1 = (String) a1;
}
}
with class A:
public class A {}
and class B:
public class B extends A {}
A variable of type A could have been assigned an instance of B, because a B is an A. Eg a Dog is an Animal, so a box labelled “Animal” could contain a dog.
But a variable of type A cannot have been assigned a String. Eg A box labelled “Animal” will not contain a Brick.
You may be asking yourself why the compiler doesn’t complain when we can see that the code will clearly fail - there’s no way the variable is a B; it’s an A!
The compiler looks only at the type of the variable when making its checks. It doesn’t examine what code came before. Although your example is simple, checking what a variable actually contains would be an impossible task in the general case.
why does the first first cast, B b1 = (B) a1;, produce a runtime error?
Variables of type A can store instances of B. However, not all instances of A are instances of B.
An A is not a B, but a B is an A. (Like, not all animals are dogs, but dogs are animals).
why does the first second cast, String b1 = (String) a1;, produce a compilation error?
A is not a supertype of String, and String is not a supertype of B.
Why is there a difference? Why doesn't the compiler see the problem in the first case?
Because variables of type A can store instances of B; but variables of type A can never store instances of String.
A variable of type A could in fact be of type B as B extends A. But a variable of type A can never be of type String. That's why the compiler can catch the cast to String, but not the cast to B.
why does the first first cast, B b1 = (B) a1;, produce a runtime error?
Because a1 is an instance of A, but is not compatible with B. Specifically, new A() creates an object that is not compatible with subclasses of A. If the runtime class (i.e., the class with which new was called) of the object is not the same as or a subclass of the target class, casting to that target class will fail at runtime. This is simply because the child class has nothing to do with that object.
why does the first second cast, String b1 = (String) a1;, produce a compilation error?
Even if the actual casting happens at runtime, the compiler performs type checks and prevents pointless operations like this. For this scenario, casting an A object to String is nonsense and the compiler can detect it: there is no relationship between String and A, and the compiler knows what class is a child of what other class. The compiler knows that there is no way in Java for a1 to be an instance of String or of a subclass of String, and that's because String is not a parent of A, the declared type of a1. There are exceptions to this, such as when the cast is begin made to an interface.
Why is there a difference? Why doesn't the compiler see the problem in the first case?
The compiler only validates type casts based on static types (the type of the variable or of the expression). It doesn't look at the runtime class, which of course isn't available until runtime when the object is actually created. When it can determine with certainty that the cast can't possibly be valid (such as in the second case), it will fail. In the first case, casting from A to B passes compilation because the declared types are compatible (i.e., an A object can possibly be an instance of B, and the compiler leaves it for the runtime to check the actual object). In the second case, the compiler knows that an A object can never be an instance of String (because String is nowhere in A's type hierarchy, and this won't change at runtime)
The class hierarchy diagram for the class A would be:
Object -> A -> B (Note that every class extends Object)
B b1 = (B) a1;
The above line compiles because B extends A and hence the compiler sees it as a valid downcast. The java compiler only checks whether it is possible for an object of type A to be of type B, by checking the class hierarchy of B (whether B extends A directly or indirectly). It doesn't check the actual type of the object A at this point. It wasn't implemented this way since it would add a lot of complexity in the compiler. Also if an object is being downcast (to call some specific sub class method perhaps), then the responsibility is on the programmer to be aware of the specific type of the object. In this example since a1 can't be cast to type B, it will be detected by the JVM at runtime.
String b1 = (String) a1;
In this case, the class String is nowhere in the class hierarchy diagram of A. Therefore it can be detected at compile time that this is an invalid cast.
Skip to the last sentence if you want to read the question right away.
Suppose we have an Interface and three classes:
interface I{}
class A implements I{}
class B extends A {}
And the following declarations:
A a = new A();
B b = new B();
Now, there's the classic way of casting which allows me to cast a reference of type A (the parent class) to an object of type B (the child class) like this:
a = b; //here a is no longer pointing at object A, and is now pointing at the same object b is pointing at.
b = (B) a; // the casting is now accepted by the compiler and during runtime as well.
Here where lies the problem though. Every time I see a line of code with multiple casting, I fail to read it (literally) and, as a result, I can't understand what it means.
For instance, let's say we have this line of code:
a = (B)(I)b;
How would you read this one? a is a reference to an object of type A, and it is being assigned the value of an object of type B (first cast from the left). But wait a minute, there's also another cast (I) preceding b. So what do we have here? Is it an interface being cast as a (B) object? or is it a b being cast as an interface which is also being cast as a (B)?
I tried to break it down to avoid confusion:
I temp = (I) b;// first line
a = (B) temp;// second line
So, first, since b is an I (because it extends A which implements I), "first line" is accepted by the compiler and during runtime.
"Second line" though, we have a reference to an object A being assigned a value of type B. At first glance, there's nothing wrong with it. But then I realized I is not an A nor is it a B, and even though the cast in "second line" can dupe the compiler into believing it's an object of type B, it shouldn't be accepted at runtime.
So the main question that I would like an answer to is how do I interpret the following line:
a = (B)(I)b;
Reality or The answer you don't want
The real problem here is that a careless goofball wrote crappy code.
The real solution is; either don't write crappy code or fix the code.
Lets keep being a goofball or The answer you seem to want
There are two types of casting in java; up-casting and down-casting.
Up-casting is when you cast an object of type A to be an instance of interface I; you are casting "up" the inheritance tree.
For example:
A aObject = new A();
I iObject = (I)aObject; // Up casting.
The benefit of up-casting is that the compiler is able to determine, at compile time, if the cast is legal.
Down-casting is when you cast an object of type I to be an object of type A; you are casting "down" the inheritance tree.
For example:
A aObject;
I iObject = new A();
aObject = (A)iObject;
The compiler does not know, at compile time, if down-casting will succeed.
Because of this, a down-cast may throw an exception at runtime.
Your confusing code: a = (B)(I)b; is an example of both up-casting (safe) and down-casting (not safe).
As a bonus, the casting is in no way required.
It is always safe to assign a B object directly to an A reference because the B class extends the A class.
Addressing: "careless goofball" seems like strong language.
It is not strong language, it is the nicest way to describe the cause your situation.
In truth, somebody who writes code like that should be terminated (optionally, get them hired by one of your competitors).
Based on the Java language grammar, the statement
a = (B)(I)b;
might be better visualized like this:
a =
// outer CastExpression
(B)(
// with its UnaryExpression being another CastExpression
(I)(b)
);
That is, it casts b to I, then casts that to B, then assigns that to an A variable.
However, it doesn't look like either of these casts are necessary. If b is an instance of B, it is also an instance of A and I.
a = (B)(I)b;
Cast b to I, and then cast it to B. Presumably since you can't cast b to B directly.
Now there aren't really good situations to use this, since casting even a single time should be avoided if possible. However if you want something like
String s = (String) myHashMap;
to compile, you need to upcast to prevent the compiler from disallowing an obviously illegal cast:
String s = (String) (Object) myHashMap;
Naturally if your myHashMap isn't null, this will cause a runtime ClassCastException, but unlike the earlier example it will compile.
Maybe you don't get the point that, even if cast to I or A, b remains an Object of type B, it doesn't loose its nature. Think of casting a way to say to java 'see my object as an object of type I'. Then if type B it is also of type A.
So the instruction tells java 'use my object as it is of type I and immediatly after use it as type B, which, by declaration, is also of type A. So no compile or runtime errors.
Then we can see.that it looks also mostly unuseful and ugly..
why can we cast from a Super Class down to a Subclass like so:
Object o = getStringObject();
String str = (String) o;
but then use the same principle to cast an interface DOWN to a sub-type E.G.?
InterfaceType anInterface;
anInterface = (InterfaceType) SubClassVar;
so EXAMPLE 1 is all fine and dandy. What i Don't understand is that if an interface is a super-type of a class that implements it, then how do we not have a ClassCastException when we downcast the class to the interface, if within the hierarchy the interface is higher? i read somewhere that there's a difference between casting via classes, and interfaces, but of course they didn't feel like explaining why, so i'm left in the open. Thanks, Stack!
Technically, you up-casted to the interface because it is higher in the hierarchy than the (presumed?) implementer of that interface, SubClassVar. Additionally that cast isn't even needed because implementations of an interface can be talked about in terms of that interface anyway, without cast syntax.
You can downcast an interface just as you did with the subclass. Here is an example:
interface I1 {}
interface I2 extends I1 {}
class I2Impl implements I2 {}
class Main {
public static void main(String[] args) {
I1 test = new I2Impl();
I2 test2 = (I2)test;
I2Impl test3 = (I2Impl) test2;
}
}
When the programmer explicitly cast one type to another, it's usually because the programmer knows more about the runtime type of the object than the compiler. The compiler could choose to allow any cast, because the smart programmer says so. However, being Java, the compiler will try to catch and prevent obvious mistakes.
A cast between two types is not allowed, if it is obvious that no object can belong to the two types at the same time; or in another word, the intersection of the two types is empty. For example, we cannot cast between String and Integer; nor String and Runnable. However, Number and Runnable can be cast to each other, because conceivably there could be an object in both types.
Also, identity comparison a==b is only allowed, if A and B can be cast to each other, for the same rationale. == isn't allowed if the compiler knows they cannot be the same object. see JLS
The exact algorithm is very complicated - http://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.5.1 Note that the algorithm is symmetric - A can be cast to B, if and only if B can be cast to A. The text of the spec is not very good and likely contains bugs (see this question )
A "downcast" is when the target type is a subtype; an "upcast" is when the target type is a supertype. Upcasting is usually unnecessary, however there are cases when it is needed.
We often end up using type casting in Java where we cast an Object of one to another in the same hierarchy.
I however could not find any material explaining how type casting worked implicitly.
Anyone here who could shed some light please.
Edit:
What I mean to ask is, given the code
Superclass a = new Subclass(); // no cast needed
Subclass b = (Subclass) a; // cast required
How does Java do the type casting from type SuperClass to SubClass.
While most posts say that Java automatically does it, I was a little curious to know what does it do automatically?
For reference types, upcasting is implicit (or at least what I think you mean by implicit—that's not standard Java terminology). There is no implicit down-casting. So if you have Superclass base class and a Subclass class that extends Superclass, you can do this:
Subclass a = new Subclass();
Superclass b = a; // no cast needed.
However, for this:
Superclass a = new Subclass(); // no cast needed
Subclass b = (Subclass) a; // cast required
Note that in general you are at risk of throwing a ClassCastException when you downcast unless you can guarantee that the downcast will work. This is typically done by using the instanceof operator:
Superclass a = new Subclass(); // no cast needed
Subclass b;
if (a instanceof Subclass) {
b = (Subclass) a; // cast required
} else {
// handle problem of a being a different subtype of Superclass
}
The standard terminology for upcasting is widening and for downcasting it is narrowing the type.
EDIT (based on edit to question):
I don't know what those posts you cite mean, unless it's the following. In Java, all reference type variables are just that—references. All that's happening when Java executes:
Subclass b = (Subclass) a; // cast required
is that it is assigning the reference stored in a to b after first checking that the object referenced by a is assignment-compatible with the declared type of b. (Note that the above does not mean that the object is of the same type as b—it could be a subtype.) The object itself is not converted in any way to a new object structure. Any changes you make to the internals object through b will be seen if you then access the object through a (provided the changes affect the more abstract view of the object represented by a). In a sense, a and b are aliases (usually with different public apis) to the same object.
You will get a run-time error if the compatibility check fails. You will get a compile-time error if the check could not possibly succeed. Thus, you cannot get this past the compiler:
String x = "Hello world";
Integer y = (Integer) x; // compiler error
The aliasing described above does not happen when casting primitive values—such as from int to long. In those cases, Java will generate a new copy of the value in the machine representation of the cast type.
I've been trying to understand casting in Java and how it affects the references. I've come up on this particular example:
public interface A1{
public void foo();
};
public class A2{
public void bar();
public static void main( String[] args )
{
A2 a = new B();
A1 c = (A1)a;
c.foo();
}
};
public class B extends A2 implements A1{
public void foo(){ System.out.println("This is B"); }
}
It prints "This is B", but i'm not sure why. This is how I understand this currently: a is a reference to an object of type A2, but during runtime, it points to a heap object which has the properties of B, but a only "sees" the properties of A2. But the types are already determined during compilation, so the cast tries to cast A2 to A1, which it can't do. Obviously I'm wrong, but I can't figure out why. Any help will be appreciated.
Casting conceptually has two components,
At runtime the JVM ensures that the object is of that type, and
will error with a ClassCastException if it is not
At compile time, it tells the compiler to allow use of the methods/fields of
that class. Note that if the object turns out to not be of that type at runtime, then it will error at runtime.
What casting does not do, is change the type of the actual object at runtime.
In your example you called new B(); that means after casting a reference from B to A1, then the object is still an instance of B. Given that foo() is declared on A1, and B extends foo then the compiler is happy (it passes 2 above). At runtime the JVM will scan B looking for the method of foo, it checks B before A1 because the object was created as type B. It did not change its type since new was called.
Casting checks are always made at runtime (see caveat in next paragraph), your reference points to an object of type B, therefore when you get to the casting bit, the VM will see a reference to an object of B, which can be safely cast. Note that casting doesn't actually change the object, it just ensures that the rest of the code calls methods which are available in the original object.
The caveat is that if it can be seen at compile time that the cast is definitely NOT possible, you do get a compile error.
String foo = "x";
Integer i = (Integer)foo; //throws a compile time error because `String` is final and `Integer` isn't its supertype.
But:
Object foo = "x";
Integer i = (Integer)foo; //this will only throw a runtime exception because from the compiler's point of view, `foo` may or may not be an integer.
The type of the variable that holds the reference has nothing to do with what the method implementations resolve to. In Java, all methods are implicitly virtual, meaning that they get looked up by the actual type of the object (instead of the type of the referring variable) every time. Since c actually points to an instance of B, it's B.foo() that gets called.
Note that this doesn't necessarily hold true for all languages - in C#, for example, methods are only virtual if you explicitly declare them as such and so i its default behavior would match what you were thinking. Java, however, is always virtual.
Reference comes into picture only during compile time or in case of static methods or behaviors.
When you are creating an object, method or behavior will depend on the whose object you have created rather than whose reference you have used.This is called polymorphism.
For example lets take an example of real life entities ---
When you rent a house, you ask a broker to find a house for you, which is your reference.but the person whom house belong, the land-lord is the actual object whom you are requesting. so it is the land-lord who gives you house on rent and not the broker.