Java Inheritance and Method Dispatch - java

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))

Related

Object evaluating in java

I don't understand, why Java acts the way it does.
I have two classes,
Class A:
public class A {
public String s = null;
public A(int s) {
this.s = "a";
}
public A(String s) {
this.s = "b";
}
public int f(A p) {
return 2;
}
}
Class B:
public class B extends A {
public String s = "c";
public B() {
super(null);
}
public B (String s) {
this (0);
this . s = s ;
}
public B (int x) {
super ("d");
this . s = this . s + x ;
}
public int f (A p) {
return 3;
}
public int f(B p) {
return 4;
}
If I know have my main class, in which I run the following code:
public class Test {
Public static main(String[] args) {
B b1 = new B("g");
A ab = new B(6);
System.out.println(ab.f(b1));
}
}
Why am I getting 3 as an result, and not 2 or 4, like I would expect?
Normally I would assume, if I run the method f of the object ab, and give f the object b1 as a parameter, it would either return 2, not compile (since the only method f in Class A uses an Object A and not an Object B) or it would look for another method f in Class B, that uses an Object B as a parameter and would execute that, in which case the program should return 4.
This is the relevant statement
ab.f(b1)
The fact that you are passing the object reference b1 to the method is irrelevant because none of these methods are doing anything with the parameters being passed. The question here is should calling f() return 3 or 2? It will return 3 because are creating an instance of B (A ab = new B(6);) and this class B overrode the f() method.
What is the impact of A ab = new B(6);?
When you instantiate objects using the superclass to the right of the assignment symbol =, you are actually widening the type of the object created (Making an object of a subclass into an object of a superclass). If the subclass have new methods, those methods will be inaccessible to this object. Only methods declared in the superclass are accessible and, through polymorphism, overridden methods are accessible as well (as was already demonstrated by the example above). However, if we were to add a new method to class B
public void newMethod() {
System.out.println("new method");
}
and modified the Test class
public class Test {
public static void main(String[] args) {
B b1 = new B("g");
A ab = new B(6);
System.out.println(ab.f(b1));
ab.newMethod(); // compile error
b1.newMethod();
}
}
newMethod will be inaccessible to instance ab but not to b1. For this reason, the method f(B p) is inaccessible for ab, as you can see in the image below.
I think it calls your f method in class B that returns 3 because, even though b1 is of class B, class B is a subclass of A. Since :
public int f (A p) {
return 3;
}
comes before the other f method, it checks that first. It says is b1 of type A? And the answer is yes, because all Bs are As. So it uses that function, returns 3 and completes the method call.

How does inheritance work in this bit of code?

So guys I've been playing around with inheritance and I've stumbled upon this program :
public class HelloWorld {
static class A {
void f() { System.out.println("A"); }
}
static class B extends A {
void f() { System.out.println("B"); }
}
static class C {
void func(B b) { b.f(); }
}
static class D extends C {
void func(A a){ a.f(); }
}
public static void main(String args[]) {
( (new D())).func( (A) (new B()));
A a = new B();
a.f();
B b = new B();
C c = new D();
c.func(b);
}
}
So how come even though A and C are implemented exactly the same way in the final few lines, A's methods
get overriden by B, but C's don't get overriden by D?
The program prints as follows :
B
B
B
Because Class D function definition is more general than C. C's function takes B type parameter but D function takes type A parameter which is a parent of B. It is more general than a function defined in C.
static class D extends C {
void func(A a){
a.f();
}
}
B b = new B();
C c = new D();
c.func(b);
Variable c is pointing to D's object so c.func(b) invokes method defined in D. A is a parent of B hence B's method is called. Same as it is called using A's reference as shown below.
A a = new B();
a.f();
It is because the method func in D does not override the same of C as the signature change.
static class C {
void func(B b) { b.f(); }
}
static class D extends C {
void func(B a){ a.f(); }
}
This will result in an override of the method

Overriding with object or string as argument

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

Cast exception error

I decided to simulate this example code from Android app:
TextView txt = (TextView) findViewById(R.id.activity_display_message);
findViewById returns View object and then we cast it to a TextView one(TextView is a subclass of View)
It seems I have misunderstood how it works. I was expecting this code to work because C extends B and therefore I should downcast a B object to C.
But I am getting an exception at runtime that I can't cast B to C.
So can anyone explain where I am wrong? And why the Android sample works?
public class A{
public static void main(String[] args){
B b = new B();
b.f();
C c = (C)b;
}
}
class B{
public void f(){
System.out.println("Class B");
}
}
class C extends B{
public void f(){
System.out.println("Class C");
}
}
Cast operation lets you change the static type of your object, which is another way of saying "tell the compiler what you know about the type of the object being cast."
If you have a variable of type B which contains an object of type C, you are allowed to cast that variable to C:
B b = new C();
C c = (C)b; // works fine
This is allowed precisely because b's object is actually a C.
When the object referenced by b is not a C, the compiler will catch your mistake, and throw a cast exception:
B b = new B();
C c = (C)b; // throws class cast exception
The difference between a successful and unsuccessful is decided by the actual type of the object at runtime. The code that works manages to cast a View to TextView because the variable which is statically typed as View actually references a TextView at runtime.
In java, you cannot assign a superclass reference variable to a subclass reference variable without a cast of the subclass type. Examples can find in When is an explicit object reference casting is required?. The compiler is happy when you explict cast the superclass reference to subclass reference, but the compiler does not care what the actual object holded by the reference. Does it actually have a superclass object, or just a superclass reference holding a subclass object? No answer from compile time but it has to answer this quesiton.
You can not just take a parent object and suddenly turn it into a child though. The parent object is not an instance of the subclass. If the actual object holded by the reference is a superclass object, casting it to a subclass reference result in a compile time error.
In your case B is parent class and C is its child.
class SuperClass {
// ...
}
class SubClass extends SuperClass {
// ...
}
public class Program {
public static void main(String[] args) {
// case 1: actual SuperClass object
SuperClass p1 = new SuperClass();
// case 2: SubClass object is referred by a SuperClass reference
SuperClass p2 = new SubClass();
SubClass s1 = (SubClass) p1; //run time error
SubClass s2 = (SubClass) p2; //OK
}
}
In Android findViewByIdreturns instance of View Class. Which is a direct superclass of TextView and other view elements.
So if you want to replicate something like that, then you can do something like this:
class Test {
public static void main(String args[]) {
A a = new A();
C c = (C)a.getViewByid('c');
c.f();
}
}
class A {
public B getViewByid(char c) {
B b = null;
switch (c) {
case 'b':
b = new B();
break;
case 'c':
b = new C();
break;
default:
b = new B();
}
return b;
}
}
class B {
public void f() {
System.out.println("Class B");
}
}
class C extends B {
public void f() {
System.out.println("Class C");
}
}

what gets typecasted when 2 classes are casted? is it the object?

I have 3 classes A,B and c as follows
A.java
class A
{
protected A(){
System.out.println("A");
}
void show()
{
System.out.println("showA");
}
}
B.java
class B extends A
{
B(){
System.out.println("B");
}
void show()
{
System.out.println("showB");
}
}
C.java
class C extends B
{
C(){
System.out.println("C");
}
void show()
{
System.out.println("showC");
}
public static void main(String... args)
{
A a= (B)new C();
a.show();
}
}
When executed gives the output
D:\java\rmi\Hello>javac C.java
D:\java\rmi\Hello>java C
A
B
C
showC
I know a superclass cannot be casted to a subclass.But in the output why is it executing the C class method (show) when there is a cast to the super class B?
A a= (B)new C();
And if this is right then what is it that is getting casted to B?
I mean here new C() would call the C constructor and hence the respective outputs but
what is the difference between new C().show(); and (B)new C().show(); what is getting casted here?
Casting an object does not change its type.
(B) new C();
will still create an object of type C, no matter what you cast it to. But if you only create a B it will of course only ever be a B which is why C’s constructor is not called when you execute
(C) new B();
The error "B cannot be cast to C at C.main" means that the superclass B is casted by C which is a subclass.(Just read it again and again and you will understand it..)
You can cast in lower element in the hierarchy with the upper element but not vice-verse..
Hope you got it :)
Class B does not extend C, so it has no knowledge of it and connot be casted to C. With java, you can down cast but not upcast.
OK:
A a= (B)new C();
-> C inherits from B, so cast is possible
Not OK
A a= (C)new B();
-> B does not inherit from C, so it cannot be cast to it
see also: Downcasting in Java
Edit:
Please consider to edit your question, as most users tend to correct an error first. (Remove the error and break it down to your original question)
"what is the difference between new C().show(); and (B)new C().show();"
This is called 'polymorphism'. there is no difference between the two calls, as java will always execute the method of the lowest level of the hierarchy.
For example:
class Bird{
public void fly(){
System.out.println("I am flying");
}}
class Duck extends Bird{
public void fly(){
System.out.println("I can not fly");
}}
class Test{
public static void main(String[] args){
Bird[] birds = {new Bird(), new Duck()};
for (Bird b: birds){
b.fly();
}
}
This would output:
I am flying
I cannot fly
When you are creating an object of a class, its superclass objects are created first.
So when you says
A a = (B)new C();
While creating object of C, its superclass objects are first created. So the object of C can be casted to B.
But in the later case while creating the object of B, it would not possible.
Always remember that a subclass object can be casted into superclass, where as a superclass object can not be casted to its subclass, which is done by you in the second case, so it gives you compilation error.

Categories