I´m having little diffculties understanding dynamic binding and inheritance in java.
Heres a liitle bit of code:
printer.print(person); // Printer is printing a person, which says: I am a Person
printer.print(specialPerson); // Printer is printing a special person, which says: I am a SpecialPerson
printer.print((Person)specialPerson); // Printer is printing a person, which says: I am a SpecialPerson
System.out.println(person); // I am a Person
System.out.println(specialPerson); // I am a SpecialPerson
System.out.println((Person)specialPerson); // I am a SpecialPerson
System.out.println(((Object)specialPerson).toString()); // I am a SpecialPerson
SpecialPerson is a child class of Person here. Both classes override the toString method. Also there is a class Printer which has 2 methods for person and specialperson objects. I understand the first 3 lines: it calls the printer class and executes the method with the matching type. The 3rd line the object is dynamically casted to a person object. But I don´t understand line 6: Why doesnt´t the casting of the object change the method which is called. Isn´t it called by the dynamic type but the static type?
If method is declared on the super object you can call it without compilation error and this is early-binding/static polymorphism. Which method will be executed depends on what the dynamic type of the object is and this is late-binding/dynamic polymorphism.
So for example:
public class Super {
public void foo(){};
}
public class Sub extends Super {
#Override
public void foo(){
System.out.println("sub");
}
public void bar(){};
}
Super s1 = new Super();
Super s2 = new Sub();
Sub s3 = new Sub();
s1.foo(); //prints nothing
s2.foo(); // prints "sub"
s3.foo(); // prinss "sub"
s2.bar(); // won't compile even though s2 is really a Sub object
In java all overridden methods are virtual methods as described here. This differs from other languages such as C++ where you need to use the virtual keyword to get this behaviour.
From Wikipedia:
Virtual functions are resolved 'late'. If the function in question is 'virtual' in the base class, the most-derived class's implementation of the function is called according to the actual type of the object referred to, regardless of the declared type of the pointer or reference. If it is not 'virtual', the method is resolved 'early' and the function called is selected according to the declared type of the pointer or reference.
Since there are only virtual methods in this case the function is called according to the actual type of the object referred to - it doesn't matter if you cast it to some base class object.
Related
What does it mean by "Casting affects the selection of overloaded methods at compile time but not overridden methods"?
I read the following passage on "Overridden methods and dynamic binding" (https://www.oreilly.com/library/view/learning-java-4th/9781449372477/ch06s01.html) and I couldn't understand the last paragraph
"In a previous section, we mentioned that overloaded methods are selected by the compiler at compile time. Overridden methods, on the other hand, are selected dynamically at runtime. Even if we create an instance of a subclass our code has never seen before (perhaps a new class loaded over the network), any overriding methods that it contains are located and used at runtime, replacing those that existed when we last compiled our code.
In contrast, if we created a new class that implements an additional, more specific, overloaded method, and replace the compiled class in our classpath with it, our code would continue to use the implementation it discovered originally. This situation would persist until we recompiled our code along with the new class. Another effect of this is that casting (i.e., explicitly telling the compiler to treat an object as one of its assignable types) affects the selection of overloaded methods at compile time but not overridden methods."
I couldnt understand the "Casting" line: "Another effect of this is that casting (i.e., explicitly telling the compiler to treat an object as one of its assignable types) affects the selection of overloaded methods at compile time but not overridden methods."
That line is referring to the fact that
overloaded versions of a method are chosen at compile time, based on the compile-time types of the arguments that you are passing; whereas
overridden methods are chosen at run time, based on the classes of the objects on which you call each method.
To understand this distinction, consider a situation where you have both overrides and overloads, like this.
public class Person {
}
---------------------------------------------------------
public class Postman extends Person {
}
---------------------------------------------------------
public class Dog {
public void barkAt(Person p) {
System.out.println("Woof woof");
}
public void barkAt(Postman p) {
System.out.println("Grrrr");
}
}
---------------------------------------------------------
public class Rottweiler extends Dog {
#Override
public void barkAt(Person p) {
System.out.println("I'm going to eat you.");
}
#Override
public void barkAt(Postman p) {
System.out.println("I'm going to rip you apart.");
}
}
In this situation, we call one of these barkAt methods, like this.
Dog cujo = new Rottweiler();
Person pat = new Postman();
cujo.barkAt(pat);
Now in this particular case, it's the compiler that chooses whether cujo.barkAt(pat); calls a method like public void barkAt(Person p) or public void barkAt(Postman p). These methods are overloads of one another.
To do this, the compiler looks at the type of the expression being passed to the method - that is, the variable pat. The variable pat is of type Person, so the compiler chooses the method public void barkAt(Person p).
What the compiler doesn't do is choose whether it's the method from the Rottweiler class or the Dog class that gets called. That happens at run time, based on the class of the object on which the method gets called, NOT on the type of the variable that you call the method on.
So in this case, what matters is the class of the object called cujo. And in this example, cujo is a Rottweiler, so we get the overridden version of the method - the one defined in the Rottweiler class.
This example will print out I'm going to eat you.
To summarise:
The overload is chosen at compile time based on the parameter type.
The override is chosen at run time based on the object class.
Now, it's possible to use casting to change the compiler's choice of overload. It's not possible to use casting to change the run time choice of override. So, we could write
cujo.barkAt((Postman) pat);
This time, the parameter passed to the method is an expression of type Postman. The compiler chooses an overload accordingly, and this will print I'm going to rip you apart..
Casting affects the selection of overloaded methods at compile time but not overridden methods
Overloaded methods are visible at compile time. But overridden methods becomes visible at runtime.
Thumb Rule:
Java calls the overridden methods based on contents of reference variable and not type of reference variables.
Below example is self explainatory. Hope it helps.
class Animal {
public void speak() {
System.out.print("Animal sounds/roars.");
}
}
class Human extends Animal {
#Override // Method is overridden
public void speak() {
System.out.print("Humans talking english.");
}
public void speak(String words) { // Method is overloaded.
System.out.print("We have brain. We are intelligent."+words);
}
}
class Earth {
public static void main(String a[]) {
Animal a = new Animal();
a.speak(); // Prints Animal sounds/roars.
Human h = new Human();
h.speak(); // Prints "Humans talking english."
Animal a = h; // Cast to superclass reference variable. However, underlying object is of Human.
a.speak(); // Prints "Humans talking english." because speak() is known by Animal at compile time. During runtime,
// the object contains the human object and hence java calls human overridden method.
a.speak("I want to be human."); // Compile time error as speak(..) is not known by Animal at compile time.
}
}
class One {
public void doThing(One o) {System.out.println("One");}
}
class Two extends One{
public void doThing(Two t) {System.out.println("Two");}
}
public class Ugly {
public static void main(String[] args) {
Two t = new Two();
One o = t;
o.doThing(new Two());
}
}
Result : One
class One {
public void doThing(One o) {System.out.println("One");}
}
class Two extends One{
public void doThing(Two t) {System.out.println("Two");}
}
public class Ugly {
public static void main(String[] args) {
Two t = new Two();
One o = t;
t.doThing(new Two());
}
}
Result : Two
I know that at runtime, even though the object reference is of the super class type, the actual object type will be known and the actual object's method will be called. But if that is the case, then on runtime the doThing(Two t) method should be called but instead the super class method doThing(One o) is called. I would be glad if somebody explained it
In the second piece of code it prints "Two".
Question : when calling from the super class reference it is calling the doThing(One o)
when calling from the sub class reference it is calling the doThing(Two o)
NOTE: I know that i am over loading and not over riding. i have edited my question for better clarity.
The method doThing() have different method signature in One and Two.
One.doThing(One one)
Two.doThing(Two two)
Since, the signature isn't matched, Two.doThing(Two) doesn't Override One.doThing(One) and since o is of type One, One.doThing() is called.
Also to be noted that One.doThing(One) can take instance of Two as an argument for One.doThing(One) as Two extends One.
Basically, "#nachokk - You are Overloading, not Overriding"
In first scenario, when you did
Two t = new Two();
One o = t;
o.doThing(new Two());
So, o is an instance of One and Two.doThing(Two) isn't available to o thus calls One.doThing(One)
In second scenario,
Two t = new Two();
One o = t;
t.doThing(new Two());
t is an instance of Two and thus Two.doThing(Two) is called.
The excelent book SCJP for Java 6 states:
If a method is overridden but you use a polymorphic (supertype)
reference to refer to the subtype object with the overriding method,
the compiler assumes you’re calling the supertype version of the
method.
So basically with using supertype for reference you're telling compiler to use supertype method.
You are just overloading,as you said
Two t = new Two();
One o = t;
o.doThing(new Two());
Even though the actual object at runtime is a Two object and not a One object, the
choice of which overloaded method to call (in other words, the signature of the
method) is NOT dynamically decided at runtime. Just remember, the reference
type (not the object type) determines which overloaded method is invoked!
When you call doThing() method with Two object argument,you will invoke One super class doThing() method.The doThing() method needs a One object, and Two IS-A One.
So, in this case, the compiler widens the Two reference to a One object, and
the invocation succeeds. The key point here is that reference widening depends on
inheritance, in other words the IS-A test.
This is something trick question
Your statment
One o = t;
I think , you are assuming that class One 'o' is equal to Class Two 't', which is not actually equal to class Two 't'
as Class Two 't' is inherited from Class One, so it will assign the base class One in your case,
So variable 't' is reference of the class One and hence will call the method of class One.
More over you create one more class named as ClassC class and try to set your statement
ClassC c= new ClassC () and then
One o = c;
You will get an error... hope the answer your question.
At the compile time the compiler searches the class One for the method doThing(Two t) but since there isn't a method with that signature it starts widening its search and finds doThing(One o). Then it holds the binary file with the descriptor one parameter whose type is One : void. At runtime since the object invoking the method is of type Two, in class Two it looks for the method that matches the descriptor and doesn't search for the method doThing that accepts an object of Two as it considers the descriptor in the binary file it links the call to the method doThing(One o).
the java specs was of great help explaining this. here is the link
I am looking at the Interface chapter provided on the Java website
Using Interface as a type
So my understanding was that the whole point of interface is that it is like a class but it's not possible to form objects from it, but this page says how to use interface as a data type. the line Relatable obj1 = (Relatable)object1; seems to create an object of type Relatable which is an interface. Although I must say that the new keyword has not been used here, thus not really creating a reference to an object of type Relatable. Is that really the cause for this line NOT creating an object of type Relatable?
Again, it further says
If you make a point of implementing Relatable in a wide variety of
classes, the objects instantiated from any of those classes can be
compared with the findLargest() method—provided that both objects are
of the same class.
What does this mean? Does this mean anything that implements Relatable can call findLargest()? If it's so, why does it say provided that both objects are of the same class?
----- EDIT -----
From the previous chapters of this tutorial:
Definition of relatable:
public interface Relatable {
// this (object calling isLargerThan)
// and other must be instances of
// the same class returns 1, 0, -1
// if this is greater // than, equal
// to, or less than other
public int isLargerThan(Relatable other);
}
Using relatable as a type:
public Object findLargest(Object object1, Object object2) {
Relatable obj1 = (Relatable)object1;
Relatable obj2 = (Relatable)object2;
if ((obj1).isLargerThan(obj2) > 0)
return object1;
else
return object2;
}
----- EDIT 2 -----
In the chapter on anonymous classes, it does this:
public class HelloWorldAnonymousClasses {
interface HelloWorld {
public void greet();
public void greetSomeone(String someone);
}
.
.
.
HelloWorld englishGreeting = new EnglishGreeting();
HelloWorld frenchGreeting = new HelloWorld() {
String name = "tout le monde";
public void greet() {
greetSomeone("tout le monde");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Salut " + name);
}
};
So how does this work?
the line Relatable obj1 = (Relatable)object1; seems to create an object of type Relatable
No. This line creates a reference (obj1) of type Relatable and assigns it to object1. In order for this to work, object1 has to be cast to the (interface) type Relatable.
No new objects are being created here.
Does this mean anything that implements Relatable can call findLargest()?
Yes.
If it's so, why does it say provided that both objects are of the same class?
It has to do with the implementation of isLargerThan(). Since any class implementing the Relatable interface can't know anything about other classes implementing it, they can't do meaningful comparisons with other classes. Therefore, in order for this to work, both objects need to be of the same class.
Response to EDIT 2
So how does this work?
Instead of first defining a class and then creating an instance of it, as in the case with the EnglishGreeting, the frenchGreeting is created on the fly. What happens under the cover is that a new class implementing HelloWorld is created, just like in the english case, only this time it is anonymous (you never get to give it a name). It is just a convenience shortcut for those times when you need a one-time implementation of an interface.
Interface types belong to the category of reference types in java. You can never instantiate an interface, but it can be assigned references to any of the objects of classes which implement it:
A variable whose declared type is an interface type may have as its
value a reference to any instance of a class which implements the
specified interface.
Interfaces are like behaviors. If a class happens to implement an interface, lets say Serializable, this adds a behavior to the class, which is, the class can be serialized.
This helps you introduce abstraction in your code. For example lets assume that you need a method in one of your utility classes which will be responsible for the actual job of serialization. Without interfaces you will end up writing a lot of methods, one for each object type that you want to serialize. Now imagine if you asked each of those objects to take care of their serialization themselves (by implementing a serialize method declared in the interface they implemented). With such implementation you need to write only one utility method for serialization. This method can take an argument of Serializable type, and instances of any class implementing this interface can be passed to the method. Now within the method you only need to invoke the serialize method on the interface variable. At runtime this will result in actual object's serialize method getting invoked.
Hope I was able to keep it simple.
Interface in Java is a mutual structure for classes that implement the interface, so the classes benefit from the methods/other member of that interface in their own way, which is called polymophism,
interface A
{
// method header only declared here, so implementation can vary between classes
public int foo();
}
class B implements A
{
public override String foo()
{
return "Class B";
}
}
class C implements A
{
public override String foo()
{
return "Class C";
}
}
so you can call foo() both from class B and C but they will react differently since they implement that method in their own way
An interface is just a class that defines the behaviour of an object, but not the underlaying implementation of it.
By making Relatable obj1 = (Relatable)object1; you are just casting the object1 to a Relatable type, and therefore you can call any of the methods defined in the Relatable interface
To your first question about Relatable obj1 = (Relatable)object1;:
A simple Relatable obj1; will not create an instance of Relatable, but specifies that any object assigned to it must be of a type implementing the Relatable-interface.
Therefore any object that is to be cast, must be of a type implementing the Relatable-interface.
Say I have two classes where SubClass inherits from (extends) SuperClass.
What is the difference between:
SuperClass obj1 = new SubClass();
and:
SubClass obj2 = new SubClass();
Both will look to the constructor of the Superclass and initialise it (the correct one, obviously). Both have access to the superclass implementations. But one is (as far as I am aware) a subclass wrapped in a superclass (the first example) and the other is a subclass object (the second example).
How will this effect the way code is run and interpreted?
What are the real world issues that a developer needs to be aware of regarding how these two objects differ?
Thanks in advance for any help!
The only difference with initializing it as a superclass is that if the subclass implementation has methods which the superclass does not, they will not be accessible via this object reference.
But "internally", this is still an instance of the subclass; so, if you call a method defined in the superclass but the subclass has overriden it, it is the subclass' method which is called: the JVM looks up methods from the more specific to the more general.
As a convoluted example, let us take Object and String:
final Object o = "Hello!"; // in fact this calls new String("Hello!")
o.toString(); // <-- uses String's .toString(), not Object's
// Can't do that: String defines .subString() but Object does not
o.subString(1);
It may help to think what the compiler knows about and what the runtime knows and a simple example:
public class Product {
public double getPrice() {...}
}
public class Book extends Product() {
public int getPageCount() {...}
}
and a simple program:
Product p = new Product();
p.getPrice(); // OK
p.getPageCount(); // compiler error
Book b = new Book();
b.getPrice(); // OK
b.getPageCount(); // OK
Product pb = new Book();
pb.getPrice(); // OK
pb.getPageCount(); // compiler error
// but we can still use the getPageCount() of pb;
((Book)pb).getPageCount(); // compiles
// however if pb was not a Book then you would get a runtime error (ClassCastException)
You can test for the actual class by:
if (pb instanceof Book) {
((Book)pb).getPageCount();
}
This is often necessary when developing classes, but if your code has a lot of instanceof it probably needs rethinking.
SuperClass obj1 = new SubClass();
What you're seeing here is a type of substitutability. What this means is, let's say you make another sub class of SuperClass, SiblingClass. Without using the superclass reference type, we would have to change more code, getters and setters, collections that might want to use it. By referencing them as the supertype, all you need to do is pass in the new SiblingClass object on construction.
The following snippet gives output as
Sup.field=0, Sup.getField()=1
I do not understand why Sup.getField() does not get 0 instead?
class Super {
public int field = 0;
public int getField(){return field;}
}
class Sub extends Super {
public int field = 1;
public int getField() {return field;}
public int get SuperField() { return super.field;}
}
public class FieldAccess{
public static void main(String[] args){
Super Sup = new Sub();
System.out.println("Sup.field ="+Sup.field + ",Sup.getField()"+Sup.getField());
}
}
Instance variables are not overridden ..they are merely hidden. Referring to the super.field refers to the actual field in the super class based on reference.
Methods are overridden and the call is made based on the object type at runtime.
All methods in java are virtual (c# term) or polymorphic by default and that's what you are seeing (the class fields are not).
When you call sup.field, it accesses field field in class Super but when you call getField() it calls the getField() method in class Sub because the instance is of type Sub.
This page has a good definition and some examples of polymorphism.
This is because,
Super Sup = new Sub();
This means that the object holds instance of the derived class object. That means, in the object memory the value of field is 1 and not 0.
So, when you run
Sup.getfield()
it runs the method of the derived class which resides in the memory that is tagged as memory for the Super class.
So, Its the difference in what it is and what it seems like.
What you are witnessing is the effect of method overriding (not to be confused with method overloading).
Even though the reference type is of type Super(), the actual method to call is resolved when the program is executing (runtime polymorphism), and because the getField() method is overridden by the subclass, that is what gets called and hence returns the value in the subtype. If you want 0 in both cases, change your instantiation to Super Sup = new Sub();