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
Related
The Java class Class<T> being Generic is confusing because of following reasons.
Imagine a class Cake<T>{}
So you can create
new Cake<Pineapple> and new Cake<Apple> etc
If each class e.x Apple has a parameterizable Class Object, and If class Class is generic, then it seems like a possibility to create Apple<Something> or Integer<Something>, which does not make any sense.
I dont know what i am perceiving wrongly here. It seems like inception, but a deeper level is the same level as above level.
Assume below generic Class
public class Cake<T> {
T cakeType;
void setCakeType(T cakeArg){
this.cakeType = cakeArg;
}
void doStuff(){
System.out.println(cakeType.toString());
}
}
Assume Pineapple is the non generic class as Parameter type for Cake
public class Pineapple {
#Override
public String toString() {
return "I am a Pineapple";
}
}
If class Class was not generic, then by intuition, there will be Pineapple Class object
But as class Class<T> itself is generic. Then it seems like a possibility to create parameterizable class object of Pineapple i.e Pineapple<Something>.
Base Assumption: There is only one Class Object for each class. What is the sense about parameterizing it ?
I'll try to tackle this from a few different approaches; I think there's a fundamental disconnect between what's happening, so a scattershot approach might have a better chance of one of the points making it all "click."
The hierarchy
So, firstly, don't think of Apple as having a Class object; rather, there is an Apple class, and, sitting next to it, a Class object which describes it. But that Class object just exists on its own, and is not part of Apple's hierarchy. It's not Apple's parent; it's its best friend.
The base class for all classes is Object, not Class. Now, if Object were parameterized, you'd have something along the lines of what you're describing -- but it's not. Since Class is not part of Apple's hierarchy, it does not logically follow that Class being parametrized makes Apple parameterized.
By analogy to other generic classes
The point of a Class object is to talk about other objects -- to tell us things like "this is a T," or "here's how you create a T." So, how does each instance tell us at compile-time which kind of things it's talking about? Via the parameter.
Similarly, the point of a List object is to talk about other objects -- to put them into a group together. And it also uses the generic parameter to describe the kinds of things it talks about. With a List, you can tell what's in it just by the type signature: a List<String> concerns Strings while a List<Integer> concerns Integers. Well, in just the same way, a Class<String> talks about Strings while a Class<Integer> talks about Integers. The fact that Class is parameterized has no more effect on String or Integer than does the fact that List is parameterized.
In other words: at the highest level, List<String> does things with Strings. So does a Class<String>
Some use cases
Finally, it may be helpful to think about why Class is parameterized. There are only a few methods on Class that return the T type. Let's take a look at two of them:
T newInstance()
T cast(Object o);
If Class were not parameterized, both of these would return Object. You'd have to do something like this:
Class<Apple> appleClass = ...;
Apple newApple = (Apple) appleClass.newInstance();
Apple existingApple = (Apple) appleClass.cast(existingObject);
Okay, as such that's not too bad. But in this case, we already knew the type of thing we were interested in (an Apple). That's why we could add the Apple casts, but by the same token, it's why using the Class<Apple> isn't useful. That snippet above would be better off just being done as:
Apple newApple = new Apple();
Apple existingApple = (Apple) existingObject;
Instead, the generic aspect of classes are most often useful when you're in a method that is itself generic. For instance:
<T> T tryConsume(Consumer<T> consumer, Object item, Class<T> consumeClass) {
T itemCasted = consumeClass.cast(item);
consumer.consume(itemCasted);
}
Granted, this example isn't very interesting. But the one thing I'll point out is that without the consumeClass, you would have to cast item to (T). Due to type erasure, this would actually be a no-op at runtime, and if the item were of the wrong class, the ClassCastException would come in from a weird, generated line within the consumer code -- not from within tryConsume, where it's nice and clear. To make that cast method actually do the cast, and do it usefully, you need consumeClass.cast(item) to return a T. And to do that, consumeClass has to be of type Class<T>.
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.
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.
I try to add an object of type SubTest to a list that has the generic type T which is defined as <T extends SubTest>. I expect this to work, but the compiler tells me that it can't convert SubTest to T. I just don't see the problem here.
Here is the full code:
public class Test<T extends Test.SubTest> {
private List<T> list = new ArrayList<T>();
public void add() {
list.add(new SubTest()); //<- compile error here
}
protected static class SubTest {
}
}
And yes, I know I could simply write
list.add((T) new SubTest());
and it would work. But the question is: why doesn't it work
without the unnecessary cast?
The type parameter T could be SubTest, but it could also be any potential subclass of SubTest. If T is such a subclass, then you can't add a SubTest to list, because it's a superclass.
Because the compiler doesn't know which class T really is, it cannot allow you to pass a SubTest to add; it may violate type safety, which Java generics helps you maintain.
The cast to T will allow you to compile it, because a cast that may work tells the compiler, "I know what I'm doing, this will work". However, this may result in a type safety violation as described above. This may manifest itself in a ClassCastException at runtime, something like "cannot cast SubSubTest to SubTest" (if T were SubSubTest, a subclass of SubTest).
Because SubTest is a superclass of T. When you declare a variable, l, to be of type List<T>, you are explicitly saying that the contents of l are at least of type T, and any client code should be able to perform all operations exposed by T on any item from the list. As T may be more specific than SubTest, you cannot add a SubTest instance to l as this instance won't necessarily obey to T's contract.
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.