How to access base class method after downcasting? - java

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.

Related

Invoking a Method of the Superclass expecting a Superclass argument on a Subclass instance by passing the an instance the of the Subclass

Can anyone please explain the output of the following code, and what's the Java principle involved here?
class Mammal {
void eat(Mammal m) {
System.out.println("Mammal eats food");
}
}
class Cattle extends Mammal{
void eat(Cattle c){
System.out.println("Cattle eats hay");
}
}
class Horse extends Cattle {
void eat(Horse h) {
System.out.println("Horse eats hay");
}
}
public class Test {
public static void main(String[] args) {
Mammal h = new Horse();
Cattle c = new Horse();
c.eat(h);
}
}
It produces the following output:
Mammal eats food
I want to know how we are coming at the above result.
Overloading vs Overriding
That's not a valid method overriding, because all the method signatures (method name + parameters) are different:
void eat(Mammal m)
void eat(Cattle c)
void eat(Horse h)
That is called method overloading (see) and class Horse will have 3 distinct methods, not one. I.e. its own overloaded version of eat() and 2 inherited versions.
The compiler will map the method call c.eat(h) to the most specific method, which is eat(Mammal m), because the variable h is of type Mammal.
In order to invoke the method with a signature eat(Horse h) you need to coerce h into the type Horse. Note, that such conversion would be considered a so-called narrowing conversion, and it will never happen automatically because there's no guarantee that such type cast will succeed, so the compiler will not do it for you.
Comment out the method void eat(Mammal m) and you will see the compilation error - compilers don't perform narrowing conversions, it can only help you with widening conversions because they are guaranteed to succeed and therefore safe.
That what would happen if you'll make type casting manually:
Coercing h into the type Horse:
c.eat((Horse) h);
Output:
Cattle eats hay // because `c` is of type `Cattle` method `eat(Cattle c)` gets invoked
Because variable c is of type Cattle it's only aware of the method eat(Cattle c) and not eat(Horse h). And behind the scenes, the compiler will widen the h to the type Cattle.
Coercing both c and h into the type Horse:
((Horse) c).eat((Horse) h);
Output:
Horse eats hay // now `eat(Horse h)` is the most specific method
Rules of Overriding
The rules of method overriding conform to the Liskov substitution principle.
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
The child class should declare its behavior in such a way so that it can be used everywhere where its parent is expected:
Method signatures must match exactly. I.e. method names should be the same as well as the types of parameters. And parameters need to be declared in the same order. It is important to note that if method signatures differ (for instance like in the code snippet provided in the question, name of one of the methods was misspelled) the compiler will have no clue that these methods are connected anyhow. I.e. it no longer be considered a case of overriding, methods will be considered to be distinct, and all other requirements listed below will not be applicable. That's why it's highly advisable to add the #Override annotation to the overridden method. With it, the compiler will give a clear feedback when it fails to find a matching method in the parent classes and interfaces, if you've misspelled the name, or declared parameters in the wrong order. Your IDE will add this annotation for you if you ask it to generate a template (shortcut in IntelliJ CTRL + O).
The access modifier of an overridden method can be the same or wider, but it can not be more strict. I.e. protected method in the parent class can be overridden as public or can remain protected, we can not make it private.
Return type of an overridden method should be precisely the same in case primitive type. But if a parent method declares to return a reference type, its subtype can be returned. I.e. if parent returns Number an overridden method can provide Integer as a return type.
If parent method declares to throw any checked exceptions then the overridden method is allowed to declare the same exceptions or their subtypes, or can be implemented as safe (i.e. not throwing exceptions at all). It's not allowed to make the overridden method less safe than the method declared by the parent, i.e. to throw checked exceptions not declared by the parent method. Note, that there are no restrictions regarding runtime exceptions (unchecked), overridden methods are free to declare them even if they are not specified by the parent method.
This would be a valid example of method overriding:
static class Mammal{
void eat(Mammal m){
System.out.println("Mammal eats food");
}
}
public class Cattle extends Mammal{
#Override
void eat(Mammal c) {
System.out.println("Cattle eats hay");
}
}
public class Horse extends Cattle{
#Override
public void eat(Mammal h) throws RuntimeException {
System.out.println("Horse eats hay");
}
}
main()
public static void main(String[] args) {
Mammal h = new Horse();
Cattle c = new Horse();
c.eat(h);
}
Output:
Horse eats hay
In your example, method overloading occurs(same method name but different parameter type passed).
When you're calling c.eat(h), the compiler will know that you want to use the void eat(Mammal m) method since your h reference has the type Mammal.
If you would change the object reference to Horse or Cattle like so:
Horse h = new Horse();
The output will be:
Cattle eats hay
This happens because the compiler will use the most specific method, in this case void eat(Cattle c), based on the object reference type Horse.
You may also be interested in method overriding which uses runtime polymorphism.

Casting an Object to an Interface

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.

Generics type erasure in Java

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.

Java: Overriding an abstract method in subclass

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;
}
}

Java generics T vs Object

I was wondering what is the difference between the following two method declarations:
public Object doSomething(Object obj) {....}
public <T> T doSomething(T t) {....}
Is there something you can/would do with one but not the other? I could not find this question elsewhere on this site.
Isolated from context - no difference. On both t and obj you can invoke only the methods of Object.
But with context - if you have a generic class:
MyClass<Foo> my = new MyClass<Foo>();
Foo foo = new Foo();
Then:
Foo newFoo = my.doSomething(foo);
Same code with object
Foo newFoo = (Foo) my.doSomething(foo);
Two advantages:
no need of casting (the compiler hides this from you)
compile time safety that works. If the Object version is used, you won't be sure that the method always returns Foo. If it returns Bar, you'll have a ClassCastException, at runtime.
The difference here is that in the first, we specify that the caller must pass an Object instance (any class), and it will get back another Object (any class, not necessarily of the same type).
In the second, the type returned will be the same type as that given when the class was defined.
Example ex = new Example<Integer>();
Here we specify what type T will be which allows us to enforce more constraints on a class or method. For example we can instantiate a LinkedList<Integer> or LinkedList<Example> and we know that when we call one of these methods, we'll get back an Integer or Example instance.
The main goal here is that the calling code can specify what type of objects a class will operate upon, instead of relying on type-casting to enforce this.
See Java Generics* from Oracle.
*Updated Link.
The difference is that with generic methods I don't need to cast and I get a compilation error when I do wrong:
public class App {
public static void main(String[] args) {
String s = process("vv");
String b = process(new Object()); // Compilation error
}
public static <T> T process(T val) {
return val;
}
}
Using object I always need to cast and I don't get any errors when I do wrong:
public class App {
public static void main(String[] args) {
String s = (String)process("vv");
String b = (String)process(new Object());
}
public static Object process(Object val) {
return val;
}
}
You don't need to do additional class casting. In first case you will always get an object of class java.lang.Object which you will need to cast to your class. In second case T will be replaced with the class defined in generic signature and no class casting will be needed.
At runtime, nothing. But at compile time the second will do type checking to make sure the type of the parameter and the type of the return value match (or are subtypes of) whatever type T resolves to (the first example also does type checking but every object is a subtype of Object so every type will be accepted).
T is a generic type. Meaning it can be substituted by any qualifying object at runtime. You may invoke such a method as follows:
String response = doSomething("hello world");
OR
MyObject response = doSomething(new MyObject());
OR
Integer response = doSomething(31);
As you can see, there is polymorphism here.
But if it is declared to return Object, you can't do this unless you type cast things.
in the first case it takes a parameter of any type e.g.string and return a type foo. In the second case it takes a parameter of type foo and returns an object of type foo.
There are few reasons that you can consider Generics over Object type in Java:
Generics is flexible and safe. At the same time, working with Object that requires type-casting is error-prone
Type Casting in Java is slow
ref : [1]: https://www.infoworld.com/article/2076555/java-performance-programming--part-2--the-cost-of-casting.html

Categories

Resources