i have the following Classes and interfaces:
public interface if1 {}
public class ifTest implements if1 {}
public class HelloWorld {
public static void main(String[] args) {
//why does the following cast not work (Compiler error)
ifTest var3 = (if1) new ifTest();
}
}
Why does this cast not work? I get the following compile error: "Type mismatch: cannot convert from if1 to ifTest"
Inheritance denotes an is-a relationship. Any object of IfTest is-an If1. However, a reference of type If1 does not necessarily refer to an object of type IfTest.
In a textbook scenario, any Dog is an Animal but any Animal is not a Dog (it might be a zebra, elephant, deer, etc).
Casting takes the form:
A a = (B) c;
There are 2 steps involved:
You have any object c that is cast to type B.
That instance of B is then assigned to variable a (where A is a type that must be either equal to B or a super class of B for the compiler to be happy and for no ClassCastExceptions to occur at runtime.
Continuing the animal metaphor, you are casting Dog to an Animal in step 1, but then trying to assign that instance of an Animal to a variable of type Dog, which the compiler knows is not allowed.
The following would work because it casts IfTest to If1 and then assigning that If1 to IfTest.
if1 var3 = (if1) new ifTest();
EDIT As Lew points out in his comment below, this is what is referred to as a widening cast (because it goes from a specific type to a more generic, "wider" type. In this instance, the cast is not necessary because the compiler knows all references to objects of type IfTest by definition are also If1.
A more practical example would be if you had a reference to the interface and wanted to cast it to a IfTest. In this case, it's a "narrowing" cast because you're going from the generic type to a more specific, "narrower" type.
if1 var1 = (if1) new ifTest();
if (var1 instanceOf ifTest) {
ifTest var2 = (ifTest) var1;
}
Note the use of the instanceof to to ensure that var1 is in fact an instance of IfTest. While we know it is, in a real-world application If1 might have multiple subtypes so this will not always be true. The check prevents the cast from throwing a run-time ClassCassException.
TLDR: The important thing to know is what is inside the parenthesis must be a type the compiler knows can be assigned to the variable being assigned to.
Here you are trying to instantiate an interface. An interface is just like a template or something similar to a contract - the class which implements the interface must obey the contract and provide implementation of the methods.
Related
Here I am trying to converting superclass object to subclass. I am getting the runtime error as "class can not be cast".
Eg :
class Animal {}
class Cat : Animal() {}
class abc {
fun abcd(): Animal {
return Animal()
}
fun getData() {
val cat: Cat = abcd() as Cat //Giving me runtime error.
}
}
You can't cast a base class 'instance' to a descendant class, because a base class does not necessarily implement the behaviors of its descendants neither knows anything about them.
In your specific example the method abcd() returns an instance of the base class Animal, and therefore such can't be cast to Cat, since Animal may not have any of the behaviors defined in Cat.
An example, imagine you had also a Dog class, and both Cat and Dog implement different methods such as dog.fetch() and cat.jump(). Such behaviors don't exist in the base class Animal, and therefore it can't be explicitly cast to a specific animal.
The opposite is valid, so casting Cat to Animal, because Cat inherits the behaviors of its base class Animal.
Instead, what you can do is to instantiate a Cat in abcd(), and still return Animal:
fun abcd(): Animal {
return Cat()
}
This is valid, and the casting will work. But, you must pay attention to avoid potential ClassCastException's at runtime if mixing up derived classes, for example if instantiating a Dog while the return type is Animal and try to use it as Cat.
Small remark: I'm assuming the reason Animal isn't open in your example is just a copy/paste mistake, as it clearly needs such keyword to allow inheritance.
Maybe what you are trying to do is something like creating a type and based on what sub-type then do something, like this:
sealed class Animal
data class Cat(val...) : Animal()
data class Dog(val...) : Animal()
class YourMapper {
fun animal(condition: Type): Animal {
return when(condition) {
... -> Dog(...)
... -> Cat(...)
}
}
fun getData(condition: Type): Animal {
return animal(condition)
}
And then the usage is
val data = YourMapper().getData(condition)
when(data) {
is Dog -> {/*do something with your dog*/}
is Cat -> {/*do something with your cat*/}
}
I know this is an old question, but it's the first Google hit for the search "Kotlin how to cast superclass to subclass", so for prosperity:
How to cast to subcalss in Kotlin
use the "as" keyword as in the original question:
if(animal is Cat) {
Cat cat = animal as Cat
}
The original question
Short answer, you can't cast a superclass object to a subclass object. Casting only changes the type of the reference to the object, the object itself remains unchanged.
The Animal class in the question should almost certainly be marked as abstract. That way there's no possibility to accidentally instantiate non-specific animals, which is what happens in the question and causes the exception.
An Animal reference variable can absolutely be cast to a Cat, provided the object it references is a Cat. But in the question a non-specific Animal is instantiated, and then later attempted to cast to a Cat, but as it is not referencing a Cat object, this understandably throws an exception.
So unlike, say, casting an Int to a Double where the cast seemingly changes the type of the object, casting object references doesn't actually "change" the object, only how finely-grained your reference to it is.
Casting cannot turn a Dog into a Cat, it can only change you from examining an animal as a generic Animal, viewing properties commont to ALL animals, to examining it as a Cat or a Dog and additionally having access to the properties only Cats or Dogs have.
Here's the code:
public class Main {
public static void main(String[] args) {
Gen<Integer> g = new Gen<Integer>(5);
System.out.println(g.getClass());
System.out.println(g.ob.getClass());
}
}
class Gen<T> {
T ob;
public Gen(T x) {
ob = x;
}
}
And here's the output
class Gen // This I understand
class java.lang.Integer // But if type erasure is happening, shouldn't this be java.lang.Object?
I get it that Type parameter T is erased at runtime, but then why is the type parameter of ob surviving at runtime?
Nope!
Consider this:
Object x = new Integer(1);
System.out.println(x.toString());
You'll get 1.
But shouldn't I get Object.toString()?
No. While x is a reference of type Object, the actual referent is an Integer, so at run-time, the Integer implementation of toString is called.
It is the same for getClass.
Type erasure is happening. Generics are a compile time type checking system. At run-time you still get the class (it is run-time type information). The linked Type erasure documentation says (in part)
Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Your instance has a type, it's Object. But an Object reference can refer to any sub-class (which is every class) in Java. You get the type it refers to.
No matter what type the variable has, the return value of getClass() depends on the variable's contents. So, since you basically have a variable Object ob which contains an Integer (your int was converted to it at the time you provided it as that constructor's parameter), the output of ob.getClass() is class java.lang.Integer.
Also, about your question as to why getClass() remembers the type argument: it doesn't. All it does is determine the content's class. For example:
class Foo {
public Foo() {}
}
class Bar extends Foo {
public Bar() {}
}
class Baz<T> {
public T object;
public Baz(T object) { this.object = object; }
}
If you now run the following snippet...
public static void main(String... args) {
Baz<Foo> obj = new Baz(new Bar());
System.out.println(obj.object.getClass());
}
You will notice that the output is not class Foo, it's class Bar.
Because when compiled, class Gen has an Object ob; The generics disappear from the final product. The angle brackets only play a role at compile-time, during static type checking. It's something the compiler can do for you to give you better peace of mind, to assure you that you're using collections and other paramterized types correctly.
the actual object assigned to ob at runtime is an instance of the Integer class, and ob.getClass() serves the purpose to find out the actual class of the object referenced by the pointer -> hence you will see java.lang.Integer printed.
Remember, what comes out is effectively class Gen { Object ob; ... }
Since my first exposure to generics was with C# , it took time get a hold of what type erasure is in java.
But after better understanding java generics , i realized that in my question i'm mixing 2 separate topics : Generics and Reflection.
The main question was , why did the second call here
System.out.println(g.getClass());
System.out.println(g.ob.getClass());
returned java.lang.Integer instead of java.lang.Object.
Looking at the docs for getClass() , the answer becomes obvious
Returns the runtime class of this Object.
so, getClass() doesn't return the type of reference but the actual object being referred to by the reference.
For ex :
Object o = "abc";
System.out.println(o.getClass());
The output wouldn't be the type of the reference java.lang.Object but rather the actual type of the object java.lang.String.
This is a Java Generics program that implements a custom interface with set(T t), get() and print(T t) methods.
The class that I use is called Animal. It takes a T argument (String in this case) and gives the animal a name.
My problem is that I can't access any of the Animal class methods in my print function.
Animal<String> a1 = new Animal();
a1.set("Mickey");
public void print(Object o) {
Animal oAnimal = (Animal) o; //Downcasting from Object to Animal.
System.out.println(o.get()); //not accessible, and I don't know how to make it accessible.
//I can only access the methods from Object, not my custom class called Animal.
}
You should use
oAnimal.get()
since that's the instance of Animal.
Remember you are not modifying o, so it will remain as an Object.
The problem is that you're still trying to call the method on o, which is still of type Object (in terms of the compile-time type of the variable, which is all the compiler cares about).
If you call the method on oAnimal instead, it will be fine:
System.out.println(oAnimal.get());
(You can't return the result of System.out.println though, as it's a void method. Nor can you specify a return value for your void method.)
Note that this has nothing to do with generics, really. The code in your question can be demonstrated simply using String:
Object o = "foo";
String text = (String) o;
System.out.println(o.length()); // Error; Object doesn't have a length() method
System.out.println(text.length()); // Prints 3
Your (edited) question does demonstrate a use of generics, but your cast to Animal is a cast to a raw type, so it's simpler just to leave generics out of the equation - the reason for the compilation failure has nothing to do with Animal being generic.
I really should know this, but for some reason I don't understand the following.
My abstract class contains the following abstract method:
protected abstract RuleDTO createRowToBeCloned(RuleDTO ruleDTO);
I also have another class as follows:
EvaluationRuleDTO extends from RuleDTO
Then in a subclass of my abstract class I have the following implementation which is not allowed due to "must override or implement a supertype method":
protected EvaluationRuleDTO createRowToBeCloned(EvaluationRuleDTO ruleDTO) {
However, the following is allowed:
protected EvaluationRuleDTO createRowToBeCloned(RuleDTO ruleDTO) {
I realize this is probably a basic question but I am a little bemused. How come I can I can return a subclass of RuleDTO in the overridden method, but I can't pass in a subclass?
Thanks
You're breaking the Liskov principle: everything a superclass can do, a subclass must be able to do. The superclass declares a method accepting any kind of RuleDTO. But in your subclass, you only accept instances of EvaluationRuleDTO. What would happen if you did the following?
RuleDTO rule = new EvaluationRuleDTO();
rule.createRowToBeCloned(new RuleDTO());
An EvaluationRuleDTO is a RuleDTO, so it must fulfill the contract defined by RuleDTO.
The method in the subclass may return an instance of EvaluationRuleDTO instead of a RuleDTO, though, because the contract is to return a RuleDTO, and EvaluationRuleDTO is a RuleDTO.
Java allows return type covariance for overrides, so you can specify the return type of an override as a more-derived type. However, Java does not allow parameter type covariance for overrides.
The reason the former is safe is that the object you return will have, at minimum, the functionality of the less-derived type, so a client relying on that fact will still be able to utilize the returned object correctly.
That's not the case for the arguments, though. If it were legal, a user could call the abstract method and pass in a less-derived type (since that's the type declared on the abstract class,) but then your derived override might try to access the argument as a more-derived type (which it isn't) resulting in an error.
In theory, Java could have allowed parameter-type contra-variance, since that is type-safe: if the overriden method only expects a less-derived argument, you can't accidentally utilize a method or field that's not there. Unfortunately, that is not currently available.
It is because when you override a method, you MUST use as parameter type the same type or a more general (wider) type, never a narrower type.
Just think about it. If you could override a method using a narrower type, you would break the polymorphism capability, don't you agree? So, doing this, you would break the Liskov Substitution Principle as JB Nizet said in his answer.
Java 1.5 has co-variant return types which why it is valid
The subclass method's return type R2 may be different from superclass
method's return type R1, but R2 should be a subtype of R1. i.e.,
subclass can return type may be a subtype of superclass return type.
In early java that was not the case, but it was changed in Java 5.0.
You cannot have two methods in the same class with signatures that only differ by return type. Until the J2SE 5.0 release, it was also true that a class could not override the return type of the methods it inherits from a superclass. In this tip you will learn about a new feature in J2SE 5.0 that allows covariant return types. What this means is that a method in a subclass may return an object whose type is a subclass of the type returned by the method with the same signature in the superclass. This feature removes the need for excessive type checking and casting.
Source: http://www.java-tips.org/java-se-tips/java.lang/covariant-return-types.html
This implies that the return types of the overriding methods will be subtypes of the return type of the overridden method.
Please see the following code:
class A {
A foo(A a) {
return new A();
}
}
class B extends A {
#Override
// Returning a subtype in the overriding method is fine,
// but using a subtype in the argument list is NOT fine!
B foo(B b) {
b.bar();
return new B();
}
void bar() {
// B specific method!
}
}
Yes ok, B IS AN A, but what happens if someone does:
B b = new B();
b.foo(new A());
A does not have a bar method.. This is why the argument can not be a subtype of the type of argument in the method being overridden.
Returning A or B in the overriding method is fine. The following snippet will compile and run just fine..
class A {
A foo(A a) {
return new B(); // B IS AN A so I can return B!
}
}
class B extends A {
#Override
B foo(A b) {
return new B(); // Overridden method returns A and
// B IS AN A so I can return B!
}
public static void main(String[] args) {
A b = new B();
final A foo = b.foo(new B());
// I can even cast foo to B!
B cast = (B) foo;
}
}
I am puzzled by generics. You can declare a field like:
Class<Collection<String>> clazz = ...
It seems logical that you could assign this field with:
Class<Collection<String>> clazz = Collection<String>.class;
However, this generates an error:
Syntax error on token ">", void expected after this token
So it looks like the .class operator does not work with generics. So I tried:
class A<S> { }
class B extends A<String> { }
Class<A<String>> c = B.class;
Also does not work, generates:
Type mismatch: cannot convert from Class<Test.StringCollection> to Class<Collection<String>>
Now, I really fail to see why this should not work. I know generic types are not reified, but in both cases it seems to be fully type safe without having access to runtime generic types. Anybody an idea?
Generics are invariant.
Object o = "someString"; // FINE!
Class<Object> klazz = String.class; // DOESN'T COMPILE!
// cannot convert from Class<String> to Class<Object>
Depending on what it is that you need, you may be able to use wildcards.
Class<? extends Number> klazz = Integer.class; // FINE!
Or perhaps you need something like this:
Class<List<String>> klazz =
(Class<List<String>>) new ArrayList<String>().getClass();
// WARNING! Type safety: Unchecked cast from
// Class<capture#1-of ? extends ArrayList> to Class<List<String>>
As for the non-reified at run-time case, you seem to have a good grasp, but here's a quote anyway, from the Java Tutorials on Generics, The Fine Print: A Generic Class is Shared by All Its Invocations:
What does the following code fragment print?
List <String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());
You might be tempted to say false, but you'd be wrong. It prints true, because all instances of a generic class have the same run-time class, regardless of their actual type parameters.
That is, there's no such thing as List<String>.class or List<Integer>.class; there's only List.class.
This is also reflected in the JLS 15.8.2 Class Literals
A class literal is an expression consisting of the name of a class, interface, array, or primitive type, or the pseudo-type void, followed by a . and the token class.
Note the omission of any allowance for generic type parameters/arguments. Furthermore,
It is a compile time error if any of the following occur:
The named type is a type variable or a parameterized type, or an array whose element type is a type variable or parameterized type.
That is, this also doesn't compile:
void <T> test() {
Class<?> klazz = T.class; // DOESN'T COMPILE!
// Illegal class literal for the type parameter T
}
Basically you can't use generics with class literals, because it just doesn't make sense: they're non-reified.
I agree with the other answers, and would like to explain one point further:
Class objects represent classes that are loaded into the JVM memory. Each class object is actually an in-memory instance of a .class file. Java generics are not separate classes. They are just a part of the compile-time type-checking mechanism. Therefore, they have no run-time representation in a class object.
There seems to be a lack in class literals in Java, there is no way to create class literals with generic information while this can be useful in certain cases. Therefore, the following code cannot be called because it is impossible to provide the class literal
class A<S> {}
<S> A<S> foo( Class<A<S>> clazz ) {}
A<String> a = foo( A<String>.class ) // error
However, my main problem was I could also not call it with a class B that extended A. This was caused by the invariance restrictions. This was solved by using a wildcard:
class A<S> {}
class B extends A<String> {}
<S> A<S> foo( Class<? extends A<S>> clazz ) { return null; }
void test () {
A<String> s = foo( B.class );
}
That said I have not found a reason what the underlying reason is that Class<A<S>>.class is invalid. Neither erasure nor bounds seem require that this is invalid.