Difference between downcasting Class vs downcasting Interface in Java? - java

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.

Related

why it is possible to new an array of an interface

Why the following is possible in Java ?
Integer[] ints = (Integer[])new Comparable[10];
But it gets ClassCastException at runtime. What is the usecase to new an array of an interface.
To answer the specific question:
Comparable toComplare[] = new Comparable[10];
Why not create an array that will allow you to store any object that implements the Comparable interface?!
The point is: the interface denotes a "common functionality" - and it could be helpful to only look at objects from that "view".
Of course, the objects stored in that array are always of some "real" class - but all these objects will implement the functionality that the Comparable interface provides.
So you could do things like:
toCompare[0] = new Integer(5);
toCompare[1] = new BigDecimal("3.2");
...
I am not saying that this is something that you would use frequently, but as said - it allows you to "collect" objects under a certain, specific "view" of their capabilities. It is also worth pointing out: having such an array does not mean that you would be able to do:
toCompare[0].compareTo(toCompare[1]);
successfully!
Beyond that: a cast always implies somehow that you, the programmer know something the compiler doesn't know. So the compiler steps back and lets you do that - assuming you know what you are doing. But as the code you are showing in the question is obviously not correct, reality comes back biting you at runtime. And yes, it would be possible to decide at compile that the given code is incorrect.
Regard this case: You have an interface and two (or more) classes that implement that interface:
interface MyInterface
{
public void someMethod();
}
class MyClass1 implements MyInterface
{
public void someMethod() { System.out.println("foo");}
}
class MyClass2 implements MyInterface
{
public void someMethod() { System.out.println("bar");}
}
And you call it like this:
public static void main(String[] args)
{
MyInterface[] array = new MyInterface[2];
array[0] = new MyClass1();
array[1] = new MyClass2();
array[0].someMethod();
array[1].someMethod();
}
An array of an interface gives you the method of holding different implementations of that interface in an array
The compiler looks at the type of the right side, and sees that it is an array of Comparable. In general, it could be an Integer[] (because that is assignable to Comparable[]).
We know that it will not be an Integer[], because that right-hand expression is a constructor call. But the compiler does not look that far. It uses the same logic as if that expression was a method call with a declared type of Comparable[]. It does not look inside to figure out the actual type.
So the compiler will accept your typecast, because it might succeed. It will only reject casts that cannot work out at all (according to declared types), such as casting Integer to String.
Note that it is probably a design flaw to allow this co-variance in arrays. You can cast Integer[] to Comparable[], but this has problems and for this reasons you cannot cast List<Integer> to List<Comparable>.
Having Integer implement Comparable, doesn't mean Integer[] implements Comparable[], so you can't convert arrays of different type. You can, however, put Integer in element of Comparable[] array.
Reason for ClassCastException is due to Heap Pollution.
Find more details here http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ050
Use case of new to an interface array / just interface is to fill it with any class object which implements that interface (or) give an anonymous inner class definition.
It is because you are performing a Narrowing Reference Conversion
The class Integer implements the Comparable interface:
public final class Integer extends Number implements Comparable<Integer>
see: 5.1.6. Narrowing Reference Conversion
From any array type SC[] to any array type TC[], provided that SC and TC are reference types and there is a narrowing reference conversion from SC to TC.
Such conversions require a test at run time to find out whether the actual reference value is a legitimate value of the new type. If not, then a ClassCastException is thrown.

Type Casting between unrelated classes in JAVA

Let there be two classes defined as follows:
Class A{
int a;
}
Class B{
int b;
}
A and B are two unrelated classes. Is there anyway I can cast an object of A to B? If yes, please explain the various options I have.
You can do
Object b = new B();
A a = (A) b;
But this will throw a ClassCastException at runtime.
Instead you can use a copy Constructor
class B {
public B(A a) { ... }
public static B toB(A a) { return new B(a); }
}
And then you can do
B b = new B(a);
or
B b = toB(a);
You can do an upcast to a common super type, followed by a downcast
(Dog)(Pet)aCat
Of course, Object is the supertype of any type, so we can use it to cast between any 2 types
(Apple)(Object)aCat
Usually, this makes no sense, and it will cause runtime exception. But it may be helpful in some generic cases. For example, Supplier<Integer> and Supplier<number> are "unrelated"; as a matter of fact, they are mutually exclusive, i.e. the intersection is empty, or, no object can belong to the two types at the same time. Nevertheless, we may want to cast Supplier<Integer> to Supplier<Number>, due to lack of variance in Java, combined with the existence of erasure. See this case study.
You can't. That would break a fundamental principle of object-oriented programming.
Though JAVA allows you to type cast object across unrelated classes i.e. which are not in class hierarchy. This is allowed because of the below case
String s = (String)list.get(1);
Well list can certainly contain different type of object and while calling such a method it could return String as well.
Coming to your question you will succeed in typecasting but at runtime you will get ClassCastException .
You can type cast object B to type A iff B IS-A A that means B is a subtype of A. If both are not in a hierarchy then type casting them does not make sense. just like you can not type cast an Animal type to Furniture type.

Why is Multiple Type-Casting Required?

Consider the following hierarchy:
TopClass
|__ MiddleClass
|__ BottomClass
Then the following code is of-course not required:
public BottomClass getBottom() {
return (BottomClass) (MiddleClass) getObject();
}
Where getObject() returns an instance of type BottomClass but has a return type of TopClass.
You could effectivily short-circuit it, and cast directly to BottomClass.
But this code raised my brows:
In the JavaFX source package,
class: com.sun.javafx.scene.control.skin.ProgressIndicatorSkin
#Override
public StyleableProperty<Paint> getStyleableProperty(ProgressIndicator n) {
final ProgressIndicatorSkin skin = (ProgressIndicatorSkin) n.getSkin();
return (StyleableProperty<Paint>)(WritableValue<Paint>)skin.progressColor;
}
Where the interface hierarchy is:
WritableValue<T>
|__ StyleableProperty<T>
And progressColor is of type StyleableObjectProperty<Paint>, that implements StyleableProperty<Paint>, but is stored in an ObjectProperty<Paint> variable, like this:
private ObjectProperty<Paint> progressColor = new StyleableObjectProperty<Paint>(null)
Any clue what's going on here?
The intermediary cast seems unnecessary. Direct cast should work.
However, the direct cast would be a "cross" cast - casting between two types that has no apparent subtype relationship.
The programmer probably doesn't want that; instead, it's more comfortable for the programmer to upcast to a nearest common supertype, then do a downcast, which feels "safer".
Discussion of casts:
It's always safe to do an "up" cast
(Animal)cat
It is allowed to do a "down" cast; the compiler assumes the programmer knows better about the actual runtime type
(Cat)animal
It is the "cross" cast that is a problem. Sometimes it is obvious that the cross-cast is impossible
(Cat)dog // Cat and Dog are two classes, and no subclass relation
(List<Cat>) listDog // List<Dog> => List<Cat>
(Runnable)fish // Fish is a final class that does not implement Runnable
However, if it is not provably incorrect, the compiler would allow it, trusting the programmer
(Runnable)animal // Animal class does not implement Runnable; but a subclass may
We can always cast between any two types by going through a common supertype
(List<Cat>)(List<?>) listDog
Of course, Object is the common supertype of all types, so we can use it to force any casts
(Cat)(Object)dog

Is Collection a subtype of Object in Java?

Is Collection<?> a subtype of Object in Java? This might be a stupid question, but isn't Object the root of every class?
No. Collection is an interface, and interfaces cannot inherit from classes -- therefore they cannot inherit from Object. (It does not make sense for an interface, which has no implementation, to inherit from a class, which can have an implementation.)
However, any class that implements Collection will obviously have to inherit from Object. So you can treat objects implementing Collection as though they do inherit from Object, because ultimately they will have to.
It's a semantic difference, but an important distinction in OO theory.
UPDATE: For those challenging this answer with the Java spec, I refer you to this code sample, which indicates that either the spec is not implemented correctly, or is just plain wrong.
TestInterface.java
public interface TestInterface {
public void foo();
}
Test.java
import java.lang.*;
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) {
Class ti = TestInterface.class;
Class sti = ti.getSuperclass();
System.out.println("superclass is null: " + (sti == null));
for (Method m : ti.getMethods()) {
System.out.println(m.getName());
}
}
}
On my system (Java(TM) SE Runtime Environment (build 1.6.0_20-b02) according to java -showversion) the output of the compiled program is:
superclass is null: true
foo
So I'm sorry, but no, interfaces do not technically inherit from anything (except possibly other interfaces) and they do not inherit the methods of Object.
Yes, Collection<?> is a reference type and therefore a subtype of java.lang.Object.
This is easy to check:
import java.util.Collection;
class Assign {
public Object check(Collection<?> things) {
return things;
}
}
Just as well, because it allows us to have collections of collections, for instance.
JLS 4.10.2 sayeth:
Given a type declaration for
C, the direct supertypes of
the parameterized type (ยง4.5)
C are all of the following:
[...]
The type Object, if C is an
interface type with no direct
superinterfaces.
Yes. Any interface type is a subtype of Object.
I think a lot of the confusion here (by which I mean cdhowie's incorrect answer and the large number of upvotes it has received) centers around an incorrect understanding of the term "subtype". "Subtype" is a compile-time concept defined by the Java Language Specification that indicates a relationship between two types. While the subtype relationship is used various places in the JLS, a type S being a subtype of a type T effectively means that an instance of S is always also an instance of T. Given that, for a reference of type S to be assigned to a reference of type T without casting and without producing a compiler error, S must be a subtype of T!
Thus, the fact that the following code is legal is actually proof that the interface type Collection is considered a subtype of the type Object by the Java langauge, just as the JLS specifies.
Collection<?> collection = ...
Object obj = collection;
cdhowie's "proof" that his answer is correct does not actually prove anything other than that the methods Class.getSuperclass() and Class.getMethods() obey the contracts they specify in their documentation... this has nothing to do with Java's concept of a "type".
Class.getSuperclass():
If this Class represents either the Object class, an interface, a primitive type, or void, then null is returned.
Class.getMethods():
This method returns an array of length 0 if this Class object represents a class or interface that has no public member methods, or if this Class object represents a primitive type or void.

Java rules for casting

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.

Categories