java method overload inheritance and polymorphism - java

Here's a test practice question i came across, would appreciate your help in making me understand the concepts
Let Hawk be a subclass of Bird. Suppose some class has two overloaded methods void foo(Hawk
h) and void foo(Bird b). Which version would get executed in the call foo(x) after the
declaration Bird x = new Hawk();
Here's the code i have so far, could someone explain to me why foo(bird b) gets executed?
public class MPractice {
public static void main(String args[]) {
Bird x = new Hawk();
Third y = new Third();
y.foo(x);
}
}
public class Third {
void foo(Hawk h) {
System.out.println("Hawk");
}
void foo(Bird b) {
System.out.println("Bird");
}
}

When Java performs overload resolution for choosing methods, it uses that type of the variable, not the runtime type of the object, to choose the method. The type of x is Bird, so the Third method chosen is foo(Bird).
This is because polymorphism isn't involved here; we're not calling a potentially overridden method on the Bird variable x, we're just calling one of a set of overloaded methods on an unrelated class, Third.

At compile time, method invocation for overloaded methods is determined based on the type the method parameters and the compile time (or static) type of the method arguments.
In your case, Third#foo(..) can take a Hawk and it can take a Bird. In your invocation
y.foo(x);
the compile time (or static) type of the argument x is Bird, since that's how it's declared, so the Third#foo(Bird) method will be invoked.

Related

Reference types in runtime

My included code's output is pretty ugly, but it's just a code for understanding how different things may work in Java. The questioned line is marked with comments on the bottom half of the code.
class CsTorta extends Torta{
public CsTorta retegez(CsTorta a){
....
}
public CsTorta retegez(Torta a){
System.out.println("This method"); //<-----it calls this one and not the one above
....
}
}
public class NewClass {
public static void main(String[] args) {
Torta tt=new Torta(5);
Torta tcs=new CsTorta(3);
CsTorta cs=new CsTorta(4);
System.out.println("");
System.out.println(tcs.retegez(tcs)); //The line in question uses the cstorta retegez method (marked with "This method")
}
}
While the tcs's type in coding-time is the reference type, in runtime when i call the tcs.retegez method it recognizes its a cstorta type, but the parameter which is the same tcs remains the reference type (thats why it uses the cstorta marked method).
My question is: Is my conclusion correct: that the program only checks the "real" type of the object if it calls a method, and uses the reference type if it does not?
That's pretty much correct. What is needed here is understanding the difference between overloading and overriding.
Overriding occurs when you have a class that declares an instance method, and a subclass that declares the same method (same name, same parameters--the result type is usually the same but could be a subclass). There are multiple methods to choose from, but the exact method is determined at run time.
public class A {
public void method1(String s1, int s2) { ... }
}
public class B extends A {
#Override
public void method1(String s1, int s2) { ... }
}
A object = new B();
object.method1("xxx",2);
The decision about which method1 is run isn't made until run time. object's real type is B, so the method1 declared in B is called.
Overloading is when two methods with the same name, but different parameters, are both present. By different parameters, I mean that the number of parameters is different, or the number of parameters is the same but the types are different. That is, they have different signatures. In that case, the decision on which method to call is made at compile time. (You can have a case where both overriding and overloading occur. The decision about which parameter signature to choose is made at compile time; but if there are multiple overriding methods with the same signature, the choice between those methods is made at run time.)
The key thing to remember is that if the decision is made at compile time, the compiler will not know what the "real" type of the object is. All it knows is how you've declared it. Thus:
public CsTorta retegez(CsTorta a){ // Number 1
....
}
public CsTorta retegez(Torta a){ // Number 2
System.out.println("This method"); //<-----it calls this one and not the one above
....
}
These are overloaded methods.
Your code looks like:
Torta tcs = // the compiler doesn't care
System.out.println(tcs.retegez(tcs));
The compiler has to decide whether to call Number 1 or Number 2. All the compiler knows is that the parameter is a Torta. The actual value could be an object of Torta, CsTorta, or any other class. Or it could be null (all right, you can't call tcs.retegez if it's null, but if you said tcs.retegez(tcs2), then tcs2 could be null.) The compiler doesn't know, and doesn't care. All it knows is that it was declared as a Torta, so it chooses the overloaded method with the Torta parameter.
(To clarify further: the compiler will choose the deepest subclass it can. Example:)
class AnotherTorta extends Torta { ... }
class YetAnotherTorta extends CsTorta { ... }
AnotherTorta t3 = // whatever
YetAnotherTorta t4 = // whatever
tcs.retegez(t3);
// since AnotherTorta can't be cast to CsTorta, it chooses the Torta parameter
tcs.retegez(t4);
// here, t4 can be cast to either a Torta or CsTorta parameter, so it chooses the subclass, CsTorta

Java casting understanding

public class Maryland extends State { Maryland() { /* null constructor */ }
public void printMe() { System.out.println("Read it."); }
public static void main(String[] args) {
Region mid = new State();
State md = new Maryland();
Object obj = new Place();
Place usa = new Region();
md.printMe();
mid.printMe();
((Place) obj).printMe();
obj = md;
((Maryland) obj).printMe();
obj = usa;
((Place) obj).printMe();
usa = md;
((Place) usa).printMe();
}
}
class State extends Region {
State() { /* null constructor */ }
public void printMe() { System.out.println("Ship it."); }
}
class Region extends Place {
Region() { /* null constructor */ }
public void printMe() { System.out.println("Box it."); }
}
class Place extends Object {
Place() { /* null constructor */ }
public void printMe() { System.out.println("Buy it."); }
}
Hi There.
I'm trying to understand the behaviour of the above main method which prints the following output when run.
Read it.
Ship it.
Buy it.
Read it.
Box it.
Read it.
I'm particularly struggling to understand the output of the last two printMe methods in the main function.
My understanding is that the first two print me operations will use there super classes printMe method as the objects have not been explicitly downcast to the sub class and thus are considered to be State and Region objects respectively by the Java compiler.
I also believe I understand the next two outputs in which the classes are downcast and thus the subclass printMe functions will override the superclass functions.
However I am struggling to understand what is occurring in the last two printMe methods. I can see that the variable obj is initially declared as an Object and then downcast as a Place is assigned a reference to the usa object(of type Place). So why is the output then of type region in this instance? I feel I am missing fundamental here in my understanding.
In Java, instance method calls follow inheritance. No matter what the reference type is, it will call the method of the type of the actual object.
The type of the reference only determine which methods the compiler knows you can call. e.g.
String hi = "Hello";
Object A = hi;
String hiToString = A.toString(); // calls String.toString();
int l = A.length(); // won't compile even though the String in A has a length() method.
Note: for static methods, the instance is ignored and it is the compile time type which determines which method is called.
In Java, non-static methods are called with late-binding, which means we cannot make sure which function it will call until run-time.
My understanding is that the first two print me operations will use there super classes printMe method as the objects have not been explicitly downcast to the sub class and thus are considered to be State and Region objects respectively by the Java compiler.
Explicitly downcast does nothing in this code. Downcast is actually performed.
Object md is still a reference to State and Object mid is a reference of Region. Compiler will never know which printMe() will be called. It only checks that State and Region classes have the function printMe() or not.
At run-time, when the md.printMe() is called, JVM will check the Runtime type information (RTTI) of md, and know it is a Object of State class. Therefore, the printMe() function in the State class is called, no matter what md is declared. (Of course, you need to override the printMe() in the State class. If you don't, State class will inherit the printMe() function from its superclass Place, and the printMe() function in Place class will be called. You can check this by removing the printMe() function in your State class.)
According to these rules, the output is reasonable.
The type cast in ((Place) obj).printMe() is needed just because in Object class, there's no printMe() function in it. Still, the compiler can't make sure which function ((Place) obj).printMe() will called, it is decided at run-time. For example, if you change your ((Maryland) obj).printMe(); to ((Place) obj).printMe();, the output is still the same.
For static methods, these rules will not fit. You can read more information about them with the keywords "Overloading, Overriding and Hiding". These terminologies will help you to understand the inheritance system in Java.

Method overloading/overriding in Java subclass object with superclass reference variable

class A{
void m1(A a) {System.out.print("A");}
}
class B extends A{
void m1(B b) {System.out.print("B");}
}
class C extends B{
void m1(C c) {System.out.print("C");}
}
public class d {
public static void main(String[] args) {
A c1 = new C(); C c2 = new C();c1.m1(c2);
}}
Output of this code is 'A'.
But if I modify class A as:
class A{
void m1(C a) {System.out.print("A");}
}
then the output is 'C'. Could somebody please explain how is this code working?
(You've made this more confusing than it needs to be by using the same inheritance hierarchy for both the parameter types and the implementation. Separating those would probably help to simplify it in your mind.)
The type of c1 is A. Therefore when working out which method signature it's going to call, the compiler can only look at methods declared in A.
So in the first case, the compiler is going to call m1(A); in the second case, the compiler is going to call m1(C).
Now in the first case, the m1(A) method is never overridden, so actually the execution-time type of c1 is irrelevant.
In the second case, m1(C) is overridden by C, so the implementation in C is called because the execution-time type of c1 is C.
So remember:
Overload resolution (which method signature is called) is determined at compile-time, based on the compile-time types of both the method target and the argument expressions
The implementation of that method signature (overriding) is based on the actual type of the target object at execution time.

Polymorphism and method overloading

I have a quick and straighforward question:
I have this simple class:
public class A
{
public void m(Object o)
{
System.out.println("m with Object called");
}
public void m(Number n)
{
System.out.println("m with Number called");
}
public static void main(String[] args)
{
A a = new A();
// why will m(Number) be called?
a.m(null);
}
}
UPDATE: actually is method with Number actually being called. Sorry about the confusion.
If I call a.m(null) it calls method with Number parameter.
My question is: why is this? where in the java language specification is this specified?
First of all, it actually calls m(Number).
It happens because both methods are applicable, but m(Number) is the most specific method, since any argument of m(Number) can be passed to m(Object), but not vice versa.
If you replace m(Object) by m(String) (or add another method such as m(Date)), compiler would report ambiguity, since the most specific method can't be identified.
See the section Choosing the Most Specific Method in the Java Specification.
This is not polymorphism or overriding. This is method overloading.
I tested this and specific method is being called (not the m(Object)) and according to the spec the specific method is always called. Which overload will get selected for null in Java?
another related question for you to think about:
public static void main(String[] args)
{
A a = new A();
Object n = new Integer(1);
a.m(n); // which method will be called?
}
My 2 cents. Method with Number argument is the one that is called, Because Number extends Object. I had a similar situation in the past, I did override a method and put Component instead of JComponent (by mistake). It took me one week to find out the reason why my method was never called. I figure it out, that if there are some inheritance relationship between the overloaded methods, the JVM matches first the deeper one in the class hierarchy.
Object is the default type in Java. If you refactor your m(Object o) method to m(String o) you'll have a compile time error saying that the call m(null) is ambiguous because Java cannot determine which class between String and Number defaults to null
Other than that, between m(Object o) and m(Number o), calling m(null) will call m(Number o) because it's the most specialized method. You would need to cast null into an Object (or anything not an instance of Number) otherwise.
a.m((String) null);

Why isn't this method chosen based on the runtime-type of its object?

Consider this:
class A {
int x =5;
}
class B extends A{
int x =6;
}
public class CovariantTest {
public A getObject() {
return new A();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
CovariantTest c1 = new SubCovariantTest();
System.out.println(c1.getObject().x);
}
}
class SubCovariantTest extends CovariantTest {
public B getObject(){
return new B();
}
}
As far as I know, the JVM chooses a method based on the true type of its object. Here the true type is SubCovariantTest, which has defined an overriding method getObject.
The program prints 5, instead of 6. Why?
The method is indeed chosen by the runtime type of the object. What is not chosen by the runtime type is the integer field x. Two copies of x exist for the B object, one for A.x and one for B.x. You are statically choosing the field from A class, as the compile-time type of the object returned by getObject is A. This fact can be verified by adding a method to A and B:
class A {
public String print() {
return "A";
}
}
class B extends A {
public String print() {
return "B";
}
}
and changing the test expression to:
System.out.println(c1.getObject().print());
Unless I'm mistaken, methods are virtual in java by default, so you're overriding the method properly. Fields however (like 'x') are not virtual and can't be overriden. When you declare "int x" in B, you are actually creating a totally new variable.
Polymorphism doesn't go into effect for fields, so when you try and retrieve x on an object casted to type A, you will get 5, if the object is casted to type B, you will get 6.
When fields in super and subclasses have the same names it is referred to as "hiding". Besides the problems mentioned in the question and answer there are other aspects which may give rise to subtle problems:
From http://java.sun.com/docs/books/tutorial/java/IandI/hidevariables.html
Within a class, a field that has the
same name as a field in the superclass
hides the superclass's field, even if
their types are different. Within the
subclass, the field in the superclass
cannot be referenced by its simple
name. Instead, the field must be
accessed through super, which is
covered in the next section. Generally
speaking, we don't recommend hiding
fields as it makes code difficult to
read.
Some compilers will warn against hiding variables

Categories