Having trouble understanding why the output is "3" and "4" for the two function calls. For,
g2.foo( t1 );
At compile time, g2 is type A, it looks for foo(type C), which it doesn't find in its main class, so it looks at its children class B, then C. So foo(...) should be bind to
public void foo(C p) {
System.out.println("5");
}
is subclass C right?
Then during run time g2 will be type C and calls foo(C p) which will result in an output of "5". I am not sure where my logic/understanding of polymorphism is wrong.
class A {
public void foo(A p) {
System.out.println("1");
}
}
class B extends A {
public void foo(B p) {
System.out.println("2");
}
}
class C extends B {
public void foo(A p) {
System.out.println("3");
}
public void foo(B p) {
System.out.println("4");
}
public void foo(C p) {
System.out.println("5");
}
}
public class HelloWorld {
public static void main(String[] args) {
A g2 = new C();
B r2 = new C();
C t1 = new C();
g2.foo(t1); // 3
r2.foo(new C()); // 4
}
}
The main thing to remember is that the overloaded method is chosen at compile time based on the compile time type of the instance for which the method is called. The run time type only determines if the chosen method is overridden by an implementation of the run time type.
g2.foo( t1 )
g2 has a compile type A, which means only public void foo(A p) can be chosen at compile time. At run time g2 in an instance of C, which means public void foo(A p) of C is called, which prints 3.
r2.foo( new C() )
r2 has a compile type B, so either public void foo(A p) or public void foo(B p) can be chosen at compile time. public void foo(B p) is the best overloaded match (since B is more specific than A). In run time, r2 is an instance of C, so public void foo(B p) of C is called, and prints 4.
Whenever a function is overridden, the virtual table (v-table) of the derived class has that function mapped with its implementation. So the function call would call the implementation as given by the v-table of that particular class.
In your case, A g2 = new C(); creates the instance of C and hence the v-table would have the foo function mapped with its (C's) implementation.
Now, when the function call happens i.e. on g2.foo(t1), the implementation of the class C will be called as it had it mapped and hence 3 is printed.
At compile time, g2 is type A, it looks for foo(type C), which it doesn't find in its main class, so it looks at its children class B, then C. So foo(...) should be bind to ...
This understanding of yours is wrong.
During compile time, since there's not foo(C) in type A, it marks that the method foo(A) will be called (Since A is a supertype of C and it's the most specific method which matches).
During runtime, C's implementation of the specific method (foo(A)) is called and prints 3. Similar for the next call too.
When u call g2.foo(t1)
g2 gets the Compile type Object of A which means that that method of A ie
public void foo(A p)
but at the runtime g2 is an Object of C Class so public void foo(A p) is called
Related
We expect "ACBD" for output with that code but we get "ACBB". Why?
class A{
public void f(Object o){
System.out.println("A");
}
}
class B{
public void f(String s){
System.out.println("B");
}
}
class C extends A{
public void f(String s){
System.out.println("C");
}
}
class D extends B{
public void f(Object o){
System.out.println("D");
}
}
public class JavaApplication40 {
public static void main(String[] args) {
A a=new C(); a.f("mee");
C c=new C(); c.f("mee");
B b=new D(); b.f("mee");
D d=new D(); d.f("mee");
}
}
I think a must call f of class A, c must call f of class C, b must call f from class B, d must call f from class D. But it looks like that it's change wit argument of function. Why?
In Java the method's identity is tied not only to its name but also to its set of parameters. That is,
public void f(Object o)
and
public void f(String s)
are separate methods in D. Java determines which one to execute at runtime, based on the type of parameter you give it. You're not actually overriding any methods in this example, just adding new ones with similar names but different parameter sets.
Note that, in all your test cases, Java chooses the object that's closest to String. If a version of the method takes a String and another version takes an Object, then the more specific String will win out.
If you instead call d.f(a), or with any other object that isn't a string, then you should see it print "D". Similarly, if you call b.f(a) with any argument that isn't a string, it should print "A".
Since class D extends class A, and you are passing String as the parameter, it will take the method with the parameter that matches your input in d.f("mee") - namely public void f(String s).
On the other hand, if in class 'D' function wasn't public void f(Object o), but public void f(String s), you would have gotten "D" as output in the log.
There is a difference between overloading and overriding. In your code you use overloading methods (creating methods with the same name but different parameter types). Overloading is resolved by compiler at compile time (unlike overriding, which is resolved at runtime).
For a, compiler sees that its type is A, so it chooses A method.
For c, compiler sees that its types is C, so it chooses the method with the most specific signature, which is C method (String is more specific than Object).
For b, compiler sees that its type is B, so it chooses B method.
For d, compiler sees that its type is D, so it chooses the method with the most specific signature, which is B method (String is more specific than Object).
Your parameter, "mee" is closest to String than to Object and therefore,
public void f(String s){
System.out.println("B");
}
is chosen over
public void f(Object o){
System.out.println("D");
}
If you are expecting ACBB, you need to define D as
class D extends B{
public void f(String s){
System.out.println("D");
}
I have a class named A, and a class named B that extends A.
Playing with some methods to understand polymorphic behaviour, I ran into a weird situation.
public class Main {
public static void main(String[] args){
B b = new B();
A a = b;
b.f1(a);
}
}
public class A {
.
.
.
public void f1(A a){
if(a instanceof B)
f1((B)a);
else
System.out.println("Nothing");
}
.
.
.
}
public class B extends A {
.
.
.
public void f1(B b){
System.out.println("B::f1(B)");
}
.
.
.
}
I expected f1 in class A to be called first(because a is of type A) which actually happened. Then I expected the line f1((B)a); to be called, since a is an instance of B. Until now everything went as expected. However, I thought that the next method that will be called is f1(B) in class B. Instead, f1(A) in class A was called over and over causing a stack overflow exception. Why wasn't the f1(B) in class B called? An instance of B was the caller and the parameter was cast to type B.
f1(A a) is an instance method of class A. It has no knowledge of methods of sub-classes of A. Therefore, it cannot call void f1(B b) of class B. Therefore, f1((B)a) executes void f1(A a) again.
If you want to call f1(B b), you'll have to call f1 on an instance variable of class B:
public void f1(A a){
if(a instanceof B) {
B b = (B)a;
b.f1(b);
} else {
System.out.println("Nothing");
}
}
Your class A has no any idea that class B exists somewhere and has B.f1(B b) function. Actually f1(A a) and f1(B b) are two different functions. what you want probably to achieve, should be done in bit different way:
public class A {
//...
public void f1(A a) {
System.out.println("Nothing");
}
//...
}
public class B {
//...
public void f1(B a) {
// this is different function, because of another parameters
}
public void f1(A a) {
if(a instanceof B)
f1((B)a);
else
super.f1(a);
}
//...
}
The code must cast the caller of the method as follow:
public class A {
public void f1(A a){
if(a instanceof B)
((B) this).f1(a);
else
System.out.println("Nothing");
}
}
In your code instead you are casting only the parameter. Doing that result in a call on the same method of the same class recursively.
If you cast the caller instead you inform the JVM that you like to call the method on the subclass, so you can exit from the method immediately.
Important Note that invoking a cast on the caller depending on the parameter can generate a class cast exception, for example in the following context:
B b = new B();
A a = new A();
a.f1(b);
You can't be sure that the method f1 receiving a B parameter is called on B object.
Given this Java code:
class A {
public void foo (Object o) { System.out.println("A"); }
}
class B {
public void foo (String o) { System.out.println("B"); }
}
class C extends A {
public void foo (String s) { System.out.println("C"); }
}
class D extends B {
public void foo (Object o) { System.out.println("D"); }
}
class Main {
public static void main(String[] args) {
A a = new C(); a.foo("Java");
C c = new C(); c.foo("Java");
B b = new D(); b.foo("Java");
D d = new D(); d.foo("Java");
}
}
why is the result ACBB?
I wil try to explain what I think, and I would appreciate if someone lets me know where my gap is.
So what I thought with the first two calls is:
a has static type A, but dynamic type C so Java should dispatch the method call dynamically and call foo() in C printing "C".
c has static and dynamic type C, so now since we inherit from A, it has to choose the most specific method, which is public void foo(String s) and thus printing "C"
b has static type B but dynamic type D so also in this case it should dynamically dispatch and call foo() in D printing "D".
d has static and dynamic type D, so now since we inherit from B, it has to choose the most specific method, which is public void foo(String o) and thus printing "B"
What is wrong in this explanation I've given here?
foo(Object) doesn't override foo (String) but overloads it. Hence D has 2 methods and since the most specific one will be used it will be foo(String) when you pass a string parameter.
From JLS 15.12.2 (emphasis by me):
This step uses the name of the method and the argument expressions to locate methods that are both accessible and applicable, that is, declarations that can be correctly invoked on the given arguments.
There may be more than one such method, in which case the most specific one is chosen.
A a = new C(); a.foo("Java"); calls foo(String) method from a which is a A
C c = new C(); c.foo("Java"); calls foo(String) method from c which is a C
B b = new D(); b.foo("Java"); calls foo(String) method from b which is a B
D d = new D(); d.foo("Java"); calls foo(String) method from d which only exists on B (D contains foo(Object))
Why should a super method hand out it's this reference as super type instead of own type?
I don't understand the practicality of this behavior. I learned to code against Types/Interfaces and not against classes, but considering this kind of behavior I'm confused about everything I thought OOP was standing for. This breaks possibility of clean code and forces me to fill it with verbose control flow like heavy use of the instanceof operator. Why does that kind of behavior make even sense?
Abstract:
Consider this code:
abstract class A {
public void visit(Target t) {
t.method(this);
}
}
If Target overloads method() with A and different child classes of A in it's signature and these children don't override visit(Target t) themselves then overloaded method(A a) will be choosen by compiler always.
Working example:
http://pastebin.com/EGNpY7pF
Code Snip
public class Target {
public static abstract class A {
void visit(Target t) {
t.method(this);
}
}
public static class B extends A {}
public static class C extends A {
#Override
void visit(Target t) {
t.method(this);
}
}
void method(A a) { System.out.println("A");}
void method(B b) { System.out.println("B");}
void method(C c) { System.out.println("C");}
public static void main(String[] args) {
Target t = new Target();
A ab = new B();
B b = new B();
A ac = new C();
C c = new C();
ab.visit(t);
b.visit(t);
ac.visit(t);
c.visit(t);
}
}
Output
A A C C
Which is really akward since ac is referenced to as A-Type but still C's overriden visit() method is called.
Your question is unclear. When a function is called on an instance of an object the child's method will be called, even if it is referenced as its parent's type; polymorphism. If the child does not override the method then the parent class' implementation of the method will be called; inheritance from hierarchy.
If a class calls its own method prefixing it with super it will find the nearest implementation up the hierarchy which matches the signature.
Suppose I have the following classes
class A{
public method(A a) {
System.out.println(3);
}
}
class B extends A{
public void method (A a) {
System.out.println(2);
}
public void method (B b) {
System.out.println(1);
}
}
A obj = new B();
obj.method( (B) obj);
((B) obj).method( (B) obj);
The first method call prints out 2 while the second method call prints out 1. Why don't both method calls print out 1?
void method (B b) of B is totally unknown for its parent A.
It's logical, because in obj.method((B) obj);, the type of obj is A which in polymorphism rule it can just call void method(A a) version of class B.
class B extends A {
// This is an overridden method visible to A
public void method(A a) {
System.out.println(2);
}
// This is an overloaded method unknown from A
public void method(B b) {
System.out.println(1);
}
}
You can read this SO answer which explained Override vs. Overload.
Because java selects the method to call in compile time. And the compiler only take into account the "left side" of an assignment.
So when you type A obj = new B() the compiler only "sees" the methods in class A.
The first method call is done using object reference of type A, so the corresponding method, which could be overridden, is called.
In the second case first the cast is done to type B, so corresponding method defined in class B ,i.e,
method (B b)
is called.