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");
}
Related
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 it is printing Integer?
At the time of compilation, the method call bound to class A's method.
I hope in B I am not overriding. creating a other method means method overloading with different classes.
but what is happening at run time?
class A{
void method(Integer i)
{
System.out.println("Integer");
}
}
class B extends A
{
void method(int i)
{
System.out.println("Int");
}
}
public class Puzzle{
public static void main(String ar[]){
A a = new B();
a.method(20);
}
}
B has two different methods called method: one declared by A, which is method(Integer), and one declared by B, which is method(int).
Since your variable a is of type A, a call to a.method() must refer to a method provided by class A, which is method(Integer).
Your methods don't share the same signature.
void method(int i) is not equal to void method(Integer i) - the first one uses primitive type. Second one uses Object Integer.
If you change method in A to method(int i) you can then override the method of A, so in your subclass:
#Override
void method(int i)
{
System.out.println("Int");
}
B doesn't override the method called "method" because it is not the same type of parameter of your method in A (Integer).
int is a primitive type while Integer is a class.
B method should be :
#Override
void method(Integer i)
{
System.out.println("Int");
}
The annotation #Override is here to tell the class should override a method. It is a good practice but not necessary (I would work without it). If you put it with your current code you would have an error because method doesn't override any method.
I hope it will help you.
This is exactly what #Override annotation is for - add #Override when you think you're overriding and the compiler will tell you if you're wrong in thinking so.
If you add #Override to the B's method(int i), the compiler will give you an error telling you that you're not in fact overriding it, because the signatures differ - namely the method has different parameter type that's not a superclass of parent's parameter type.
The signatures of the method of A and that of B are not the same. The method in A takes an object integer (Integer) as parameter while that of B takes a primitive integer (int) as parameter. So you are not really overriding the method of A in B. To do so, change the signature of the method B to be like that of A.
class A{
void method(Integer i){
System.out.println("Integer");
}
}
class B extends A{
#Override
void method(Integer i){
System.out.println("Int");
}
}
public class Puzzle{
public static void main(String[] args){
A a = new B();
a.method(20);
}
}
Your reference is of type A - it does not recognize methods declared in type B in compile time, even though the may exist in runtime. The int literal 20 is then autoboxed to a java.lang.Integer, and method(Integer) is called.
If you were to declare the reference as B, the method(int) would be a better fit to your argument, and would be have been called.
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
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.
My code looks sort of like this, but this is a simplified version:
class A:
public class A{
public void testArgs(A a){
System.out.println("A");
}
public void test(){
System.out.println("A");
}
}
class B:
public class B extends A{
public void testArgs(B a){
System.out.println("B");
}
public void test(){
System.out.println("B");
}
}
class Main:
public class Main{
public static void main(String[] args){
a(new B()).testArgs(new B()); // prints A
(new B()).testArgs(new B()); // prints B
a(new B()).test(); // prints B
}
public static A a(B b){
return b;
}
}
Why does a(new B()).testArgs(new B()) print A not B?
Is there some sort of way to workaround/fix this?
edit:
Clarification:
What I really want is the superclass method to be run when it is called with an A, and the subclass method to be run when testArgs is called with a B.
Casting also isn't an option because in the actual code, unlike here, I don't know whether the result of the method call is actually B or not.
edit:
Solution:
Thanks everyone for your answers. Thanks for the clarification on overriding. I used this to implement the desired behavior.
For anyone who has a similar problem in the future:
Change class B to
public class B extends A{
public void testArgs(A a){ // Corrected overriding, thanks
if(a instanceof B) // Check if it is an instance of B
System.out.println("B"); // Do whatever
else // Otherwise
super.testArgs(a); // Call superclass method
}
public void test(){
System.out.println("B");
}
}
The two testArgs functions are different. One takes an A and the other takes a B. Thus, the B version doesnt override the A version. Since a(new B()) is of type A and B extends A, it is the A version that will run.
There is a "workaround":
public class B extends A{
public void testArgs(A a){ // <-- note how this is A a, not B a
System.out.println("B");
}
public void test(){
System.out.println("B");
}
}
Then you will see B for all 3 cases (because B's testArgs will override A's testArgs)
The call a(new B()) returns a new B instance of type A, when invoking testArgs() it gets called in class A and prints "A". Correct.
Should the method testArgs() override the one in super class, then the sub class version will get invoked, by means of polymorphism (but its not your case).
So to get your expected result, class B needs to properly override the method in super:
public class B extends A{
public void testArgs(A a){ // polymorphism would work
System.out.println("B");
}
...
Debugging the problem at run-time is rather difficult. There's a very simple way to let the compiler warn you by using the anotation #Overrides
public class B extends A{
#Overrides
public void testArgs(B a){
System.out.println("B");
}
#Overrides
public void test(){
System.out.println("B");
}
}
If I have inadvertently left out a parameter or used a wrong one then #Overrides will catch my error at compile time itself.
In your first statement, that prints "A", you could workaround the issue by casting the result of the static a() call to a B:
((B) a(new B())).testArgs(new B());