What is the difference between compile time linking and run time linking? - java

I am currently reading a book and stuck at following code:
public class TestAnimals {
public static void main (String [] args ) {
Animal a = new Animal();
Animal b = new Horse();
a.eat(); // Runs the Animal version of eat()
b.eat(); // Runs the Horse version of eat()
}
}
class Animal {
public void eat() {
System.out.println("Generic animal eating generically");
}
}
class Horse extends Animal {
public void eat() {
System.out.println("Horse eating hay, oats, horse treats");
}
public void buck() {
}
}
Please have a look at the commented lines.
The book goes on to say "To reiterate, the compiler looks only at the reference type, not the instance type". Really? Had this been the case, both a.eat() and b.eat() would produce same result, as they (a and b) have same reference type (which is Animal).
Also to me this seems to be the compile time binding because virtual keyword has not been used but in the book results are of run time binding. I am so confused at this point. Any help would be greatly appreciated.

The compiler does indeed look only at the statically known type, not the actual runtime type of the instance - after all, Java is a statically typed language. As a matter of fact, in all but the most trivial cases, the compiler can't even know the runtime type of an object reference (as to solve this problem for the general case, it would have to solve undecidable problems).
The point the book is trying to make there is that this snippet would fail to compile:
b.buck();
Because b is of (compile-time) type Animal and Animal doesn't have a buck() method. In other words, Java (like C++), will verify at compile time whether the method call makes any sense, based on the information it has about the type of the variable.
Now the reason the book's results correspond to runtime binding is precisely because you have runtime binding at that call spot: in Java (unlike in C++), all non-static methods are virtual by default.
Thus, there's no need for a virtual keyword that would allow you to explicitly opt in to polymorphism semantics (as you would in C++ and C#, for example). Instead, you can only prevent any further overrides of your methods by individually marking them as final or marking their containing class as final (if the latter is applicable in your case).

#Sandeep - regarding your latest comment (at the time of this writing)...
If in Java, all non-static methods are virtual by default,
why the does the books says "To reiterate, the compiler
looks only at the reference type, not the instance type"?
Isn't this statement amounting to compile time binding?
I think the book is a bit incomplete...
By 'reference type' the book is talking about how a given variable is declared; we can call that the variable's class. One thing that will help you coming from C++ is to think of all Java as variables as pointers to a particular instance (except primitive types like 'int'). It is easy enough to say everything in Java is "pass by value", but because variables are always pointers it is the pointer value that gets pushed onto the stack whenever a method call is made... the object instances themselves stay in the same place on the heap.
This is what I was originally writing before I noticed the comment...
The ideas of "Compile time" and "run time" are not that helpful (for me) for for predicting behavior.
I say that because a more useful question (for me) is "How do I know what method will be called at runtime?"
And by "How do I know" I mean "How do I predict" ?
Java instance methods are driven by whatever the instance actually is (virtual functions in C++).
An instance of Class Horse instance will always be a Horse instance.
The following are three different variables ("reference types" to use the books phrasing) that all happen to refer to the same instance of Horse.
Horse x = new Horse();
Animal y = x;
Object z = x;
Java class methods (basically any method w/'static' in front of it) are less intuitive and are pretty much limited to the exact class they refer to in the source code, which means "bound at compile time."
Consider the test output (below) when reading the following:
I added another variable to your TestAnimals class, and played with the formatting a little...
In main() we now have 3 variables:
Animal a = new Animal();
Animal b = new Horse();
Horse c = new Horse(); // 'c' is a new variable.
I tweaked the output of eat() a little bit.
I also added a class method xyz() to both Animal & Horse.
From the printouts you can see they are all different instances.
On my computer, 'a' points to Animal#42847574 (yours will say Animal#some_number, the actual number will vary from one run to the next).
'a' points to Animal#42847574
'b' points to Horse#63b34ca.
'c' points to Horse#1906bcf8.
So at the beginning of main() we have one 'Animal' instance and two different 'Horse' instances.
The big difference to observe is how .eat() behaves and how .xyz() behaves.
Instance methods like .eat() pay attention to the instance's Class.
It doesn't matter what Class the variable is that is pointing to the instance.
Class methods, on the other hand, always follow however the variable is declared.
In the example below, even though Animal 'b' refers to a Horse instance, b.xyz() invokes Animal.xyz(), not Horse.xyz().
Contrast this with Horse 'c' which does cause c.xyz() to invoke the Horse.xyz() method.
This drove me CRAZY when I was learning Java; in my humble opinion it was a cheap way to save a method lookup at runtime. (And to be fair, in the mid 1990's when Java was being created, maybe it was important to take performance shortcuts like that).
Anyway, may be more clear after I reassign Animal 'a' to the same Horse that 'c':
a = c;
Now a and c point to same instance:
Animal a=Horse#1906bcf8
Horse c=Horse#1906bcf8
Consider the behavior of both Animal 'a' and Horse 'c' at after that.
Instance methods still do whatever the instance actually is.
Class methods still follow however the variable is declared.
=== begin example run of TestAnimals ===
$ ls
Animal.java Horse.java TestAnimals.java
$ javac *.java
$ java TestAnimals
Animal a=Animal#42847574
Animal b=Horse#63b34ca
Horse c=Horse#1906bcf8
calling a.eat(): Hello from Animal.eat()
calling b.eat(): Hello from Horse.eat()
calling c.eat(): Hello from Horse.eat()
calling a.xyz(): Hello from Animal.xyz()
calling b.xyz(): Hello from Animal.xyz()
calling c.xyz(): Hello from Horse.xyz()
Now a and c point to same instance:
Animal a=Horse#1906bcf8
Horse c=Horse#1906bcf8
calling a.eat(): Hello from Horse.eat()
calling c.eat(): Hello from Horse.eat()
calling a.xyz(): Hello from Animal.xyz()
calling c.xyz(): Hello from Horse.xyz()
$
=== end example run of TestAnimals ===
public class TestAnimals {
public static void main( String [] args ) {
Animal a = new Animal( );
Animal b = new Horse( );
Horse c = new Horse( );
System.out.println("Animal a="+a);
System.out.println("Animal b="+b);
System.out.println("Horse c="+c);
System.out.print("calling a.eat(): "); a.eat();
System.out.print("calling b.eat(): "); b.eat();
System.out.print("calling c.eat(): "); c.eat();
System.out.print("calling a.xyz(): "); a.xyz();
System.out.print("calling b.xyz(): "); b.xyz();
System.out.print("calling c.xyz(): "); c.xyz();
a=c;
System.out.println("Now a and c point to same instance: ");
System.out.println("Animal a="+a);
System.out.println("Horse c="+c);
System.out.print("calling a.eat(): "); a.eat();
System.out.print("calling c.eat(): "); c.eat();
System.out.print("calling a.xyz(): "); a.xyz();
System.out.print("calling c.xyz(): "); c.xyz();
}
}
public class Animal {
public void eat() {
System.out.println("Hello from Animal.eat()");
}
static public void xyz() {
System.out.println("Hello from Animal.xyz()");
}
}
class Horse extends Animal {
public void eat() {
System.out.println("Hello from Horse.eat()");
}
static public void xyz() {
System.out.println("Hello from Horse.xyz()");
}
}

This question can be re-phrased as difference between Static binding & Dynamic binding.
Static binding is resolved at compile time and Dynamic binding is resolved at run time.
Static binding uses type of "Class" (reference as per your example) and Dynamic binding uses type of "Object" (instance as per your example). private, final ,static methods are resolved at compile time.
Method overloadingis an example ofStatic binding&Method overridingis example ofDynamic binding`.
In your example,
Animal b = new Horse();
b.eat();
resolution of Object on which "eat()" method has to be invoked happens at runtime for Animal b. During runtime, Animal b has been resolved to Horse type and Horse version of eat() method has been invoked.
Have a look at this article for better understanding.
Have a look at related SE question : Polymorphism vs Overriding vs Overloading

Related

Superclass obj = new subclass(); meaning and casting

If I have super class (Animal) and a sub class (Cat).
What does the third point mean? And when we have to cast?
Cat obj = new Cat(); means creating an object from Cat class
Animal obj = new Animal(); means creating an object from Animal class
Animal obj = new Cat();
First lets understand Class, reference and Object. Suppose we have a class named SomeClass
SomeClass ref = new SomeClass();
Above we have an Object of SomeClass created in Heap and a reference variable refers to it. We have named the reference variable as ref. Object is present in heap and we can just access it using a reference. So Object type is of the actual class (on which new keyword has been applied). Reference variable type can be of actual class or its Parent class.
Now let us see the relationship of Inheritance. A class inheriting from another class share a Child-Parent relationship.
Child inherits the behaviour of its Parent and can then override some of the behaviour and also can add some additional behaviour. Hence Object of Child can be used at any place where Parent object is expected, as Child has all the behaviour of its Parent so invoking any behaviour present in the Parent will be handled by the Child.
Parent class do not know about the additional behaviour of its child class ( child class is written later in time.) Hence object of Parent can not be used at the places where Object of Child is expected (If additional behaviour of Child is invoked on Parent object then it will not be honoured).
Now let us assume we have classes ParentClass and ChildClass such that ChildClass inherits ParentClass
ParentClass reference = new ParentClass(); // Valid
ParentClass reference = new ChildClass(); //Valid
ChildClass reference = new ChildClass(); //Valid
ChildClass reference = new ParentClass();// Not Valid.
Note that
ParentClass reference = new ChildClass(); // Here Object is of type ChildClass and Reference is of type ParentClass.
Now when to cast. Any place expecting the object of ParentClass, there is no need to cast, both the objects (of ParentClass or of ChildClass) are fine.
Any place expecting the Object of type ChildClass but if we have a case like below then casting is needed.
public void someMethod(ChildClass expected){
//some implementation
}
ParentClass ref = new ChildClass();
someMethod(ref);//Invalid : Compilation Issue
someMethod((ChildClass)ref);// Valid
ParentClass anotherRef = new ParentClass();
someMethod(anotherRef); // Invalid : Compilation Issue
someMethod((ChildClass)ref); //Invalid, compiles but Runtime it will fail.
Thumb rule : Child is Child, Child is Parent, Parent is Parent , Parent is not Child.
Another example for understanding.
public abstract class List{
public abstract void add(int element);
public abstract void remove(int element);
public int size();
}
public class Application{
private List listReference;
public void setList(List ref){
listReference = ref;
}
}
//Now you may create sub classes as below
public class ArrayList extends List{
// all the abstract methods of List have been implemented
}
public class LinkedList extends List{
//all the abstract methods of List have been implemented
}
Now in main method you can pass ArrayList or LinkedList or any other implementation.
public class Init{
public static void main(String[] args){
Application app = new Application ();
app.setList(new LinkedList());
//or you can set it like this
List listRef = bew ArrayList();
app.setList(listRef);
//or you can set it like this
LinkedList linkedListRef = new LinkedLiet();
app.setList(linkedListRef);
}
}
Notice that the method setList() accepts List type of reference and we can provide any implementation of the List abstraction. This leads to a flexible design.
Classes should be dependent on the abstraction. Programming to interface is a Design Principle which leads to easy maintenance of the application code.
The reason why this is confusing on the face of it is that it is not something that you would typically do in real code, except in the case of a Factory.
As hinted at in Zabuza's comment, you can do this because a Cat 'is-a' kind of Animal and so you can assign an object of type Cat to an object of type Animal. But you can't do the assignment the other way of course, because an Animal is not a kind of Cat.
Now, there are some lurking issues that come with actually being able to create an instance of the the supertype as well as the subtype that mean you typically wouldn't actually do this in real code because it complicates a lot of things down the road. What you would more likely do is make Animal an interface and have a GenericAnimal class that implements it, along with having Cat implement it.
Say you have an object that represents a zoo, and most zoos typically have a collection of animals. The most obvious way to represent this would be this:
java.util.Collection<com.myproject.Animal> zooAnimals;
So now imagine the zoo builds a new habitat, and it's for a lion. For the sake of the story assume we have a very lazy data model and instead of having a specific com.myproject.animals.cats.Lion subtype we just said "lions are cats, close enough". So to update the data structure that tracks all the animals and their names and addresses and favorite foods and whatever else, we might do this:
com.myproject.Animal newArrival = new com.myproject.animals.Cat("Larry the Lion", "Africa Exhibit", "Gazelles");
zooAnimals.add(newArrival);
Now imagine that the zoo continues to grow, and gets an Ostrich in the Africa habitat. And the same lazy data model applies so we just call it a Bird.
com.myproject.Animal newArrival = new com.myproject.animals.Bird("Oliver the Ostrich", "Africa Exhibit", "Whatever Ostriches Eat");
zooAnimals.add(newArrival);
Now actually writing that exact code would normally only happen in very specific cases inside a factory object or something, and realistically type hierarchies like this have a tendency to not work very well in practice at all, contrary to what a lot of us learned in Object Oriented Programming class, but for the sake of the question that is an example situation where you might do what you are asking about.
Lastly, you also asked when you have to cast. You would have to do this if you had code that needed to know about any special methods or fields that the Cat or Bird types have that Animal does not have. For instance the Cat type might have a property called tailLength because cats typically have tails and for whatever reason the zoo likes to keep track of that. Similarly the Bird type might have a property called wingSpan because birds have wings and we want to keep track of how big they are. The Animal type doesn't have any of these properties so if we get the object for the lion or the ostrich out of the zooAnimals collection (and maybe we looked at the name or something to figure out it was the lion) we would have to cast back to the Cat type in order to access the tailLength property. Same thing for the ostrich and it's wingspan.
for( Animal theAnimal : zooAnimals ){
if( theAnimal.getName().equals("Larry the Lion") ){
Cat theCat = (Cat)theAnimal;
System.out.println("Larry's tail is " + theCat.getTailLength() + " inches long";
}
else if( theAnimal.getName().equals("Oliver the Ostrich") ){
Bird theBird = (Bird)theAnimal;
System.out.println("Oliver's wingspan is " + theBird.getWingSpan() + " inches";
}
}
Again you probably wouldn't actually do something like that in real code, but perhaps it helps to illustrate the example.

Why downcasting is allowed in java?

class Animal{
public void findAnimal(){
System.out.println("Animal class");
}
public void sayBye(){
System.out.println("Good bye");
}
}
class Dog extends Animal{
public void findAnimal(){
System.out.println("Dog class");
}
}
Given the inheritance above ,it is understood that a reference of Animal can refer to an object of Dog
Animal animal=new Dog();
As a Dog object can perform everything an Animal can do like in above case a Dog also have sayBye and findAnimal methods.
But why it is allowed to downcast an Animal object to a Dog object which serves no purpose and fails at runtime.
Dog dog=(Dog)new Animal(); // fails at runtime but complies.
Dog dog=(Dog)animal;
The above statement look logical as the animal reference is pointing to a Dog object.
This sort of casting is allowed for situations when you get an object of a superclass from outside code, e.g. as a parameter to your method, and then you need to call methods specific to a subclass.
This is not a good practice, but in some rare situations you are forced to do things like that, so the language allows it:
void sound(Animal animal) {
if (animal instanceof Dog) {
Dog dog = (Dog)animal();
dog.bark();
}
if (animal instanceof Cat) {
Cat cat = (Cat)animal();
cat.meow();
}
}
why it is allowed to compile Dog dog=(Dog) new Animal()
Because compiler designers decided to not detect this error at compile time. They verified that the expression being cast to Dog is of type that is a superclass of Dog, and allowed the expression to compile. They could go further and check that the expression will always result in an exception, but that would require an additional effort for very little improvement in user experience with the language.
Because you need it sometimes.
Especially when Java did not yet have generics (Java 1.4 and older), you almost always needed to cast when you got for example an object out of a collection.
// No generics, you don't know what kinds of objects are in this list
List list = new ArrayList();
list.add(new Dog());
// Need to cast because the return type of list.get() is Object
Dog dog = (Dog)list.get(0);
Since we have generics since Java 5, the need for casting is greatly reduced.
You should try to avoid casting in your code as much as possible. A cast is a way to deliberately switch off the compiler's type checking - in general you don't want to do that, you want to make use of the compiler's checking instead of circumventing it. So, if you have code where you need to cast, think a bit further to see if you can write it without the cast.
You need that capability to access an earlier cast object as its original type.
For example, if you cast a Dog to an Animal to pass it to a generic processor, you may later need to cast it back to a Dog to perform specific methods.
The developer is responsible to make sure the type is compatible - and when it is there will be no error. Some pseudo code:
public void example(Animal foo){
if( ...condition... ) ((Dog)foo).bark();
else if( ...other condition... ) ((Cat)foo).meow();
}
Since the introduction of generics, this is less commonly used, but there are still cases for it. The developer is solely responsible for guaranteeing the type is right if you don't want an error.
case 1 -
Here we use loose coupling.
Animal animal = getSomeDog(),
Dog dog = (Dog) animal; // this is allowed because animal could reference a dog
case 2
Here we you use tight coupling.
Animal animal = new Animal();
Dog dog = (Dog) animal; // this will fail at runtime, because animal doesn't reference a Dog
We use Downcasting when there is possibility to succeed at run time
so case 1 has possibility to succeed at runtime over case 2
Down casting is considered as a bad Object Oriented practice. It must be avoided to as much extent as possible.
Java still has it and your question is a good question as why Java allows Down-casting.
Suppose a case below.
public interface List{
public boolean add(Object e);
public boolean remove(Object o);
}
public class ArrayList implements List{
// Extra method present in the ArrayList and not in the parent Interface
public Object[] toArray() {
// returns array of the objects
return Arrays.copyOf(elementData, size);
}
#Override
public boolean add(Object e){
// add e to the ArrayList Underlying array
}
#Override
public boolean remove(Object o){
// remove o from the ArrayList Underlying array
}
}
A good Object oriented practice is to Code for Interfaces. But often there are methods defined in the concrete implementations which we need to call. I read an comment from some one and I quote it in my words.
Know the Rules, in case you need to break them Do break them Knowingly and take care so as to prevent from any adverse effect.
Below is an example where we need to do the Down-casting. The example of down-casting in your question is to teach what is down-casting, below is real life example.
public void processList(List items){
items.add( new Object() );
items.add( new Object() );
processAsPerTypeOfList(items);
}
public void processAsPerTypeOfList( List items ){
if( items instanceof ArrayList){
Object[] itemArray = ((ArrayList)items).toArray();// DOWNCASTING
// Process itemArray
}
}
For more reference you can also see a related question : Why Java needs explicit downcasting?

What is the class type of a superclass ref pointing to a subclass object?

I have the following codes:
1. public class Tester
2. {
3. public static void main(String[] args)
4. {
5. A a = new B();
6. System.out.println(a.getClass()); //Prints class B
7. System.out.println(a instanceof A); //Prints true
8. System.out.println(a instanceof B); //Prints true
9. System.out.println(a.valA); //Prints 1
10. System.out.println(a.valB); //Compilation error
11.
12. }
13. }
14.
15. class A
16. {
17. int valA=1;
18. }
19.
20. class B extends A
21. {
22. int valB=2;
23. }
At line 6 it shows that a is of type class B. However when it reaches line 10, the compiler produces an error: location: variable a of type A.
So my question is: What exactly is the class type of a now? Why getClass() shows that it is of type class B, yet the compiler complains it as type A during compilation?
Further more, since a instanceof B is true, why can't I access valB?
To make things clearer:
EDIT: I ran this statement: System.out.println(a); and the output was B#36d98810 which somehow proves that the toString() method of class B was executed. Since variable a can access the toString() method within class B, why can't it access valB which also resides in class B?
Professor Jonathan Shewchuk from UC Berkley explains about shadowing over here. Start at 18 minutes. (If the link changes just google search for CS 61B Lecture 15: More Java)
To answer your question in short there are two types for a variable, static type and dynamic type.
Static type is its Type at compile time
Dynamic type is its Type at run time.
In your example
A a = new B();
The static type of a is A and the dynamic type of a is B.
In Java a variable gets its non static methods from dynamic type
(if the method exists in both the parent and child class)
and
its fields and static methods from the static type.
This is true in C# only if the method is overridden in the sub class
Update:
The line
a instanceof A
tells you whether the dynamic type of a is of type A OR a subclass of A
Update 2:
AN example that illustrates this
public class PlayGround {
public static void main(String[] args) {
Animal a = new Dog();
System.out.print(a.name);// displays animal
System.out.print("\r\n");
a.MakeStaticSound();// displays static animal sound
System.out.print("\r\n");
a.MakeSound();// displays bow wow
}
}
class Animal {
public String name = "animal";
public void MakeSound() {
System.out.print("animal sound");
}
public static void MakeStaticSound() {
System.out.print("static animal sound");
}
}
class Dog extends Animal {
public String name = "dog";
public void MakeSound() {
System.out.print("bow wow");
}
public static void MakeStaticSound() {
System.out.print("static bow wow");
}
}
Please note that the more readable and preferred way to call a.MakeStaticSound() is Animal.MakeStaticSound()
a is not an object. It's a variable.
The type of the variable is A. The type of the object that the value of the variable refers to at execution time is B.
The compiler resolves everything against the compile-time type of the expressions involved - the variable in this case. When trying to resolve the name valB within the compile-time type of A, it fails to find anything - hence the error.
You need to keep in mind that compilation and execution are two different processes that happen at different times and have different kinds of information available to them. The compiler has to predict the future -- it has to decide whether it can guarantee that your code will make sense in the future, at runtime. It does this by analyzing the types of the objects in your code. The runtime, on the other hand, just has to inspect the current state of things.
When you read the line A a = new B(), you are inferring more information about the a local variable than the compiler is. The compiler basically just sees this as A a = <some expression>. It does not take note of the contents of the expression that's used to produce the value for a.
The fact that you've said A a = ... is you telling the compiler: "hey, this a thing I'm going to deal with in the rest of my program, it's just an A, don't assume anything more about it." If you had instead said B a = ..., then you're telling the compiler that it's a B (and the compiler also sees B extends A elsewhere in your code, so it knows it's also an A).
The subsequent expressions a instanceof A, a instanceof B, a.getClass(), and a.toString() are legal, from the compiler's point of view, regardless of the type of a: the instanceof operator and the getClass() and toString() methods are defined for all Objects. (The compiler does not need to predict what value those expressions will produce at runtime, just that they will produce either true or false, some Class<?>, and some String, respectively.)
But then when you come to a.valA and a.valB, the compiler actually has to do some real work. It needs to prove or guarantee that the a object will have a valA and a valB field at runtime. But since you've explicitly told it earlier to just assume that a is an A, it can not prove that it will have a valB field at runtime.
Now, later on, at execution time, the JVM has more information. When it evaluates a.getClass(), it actually looks up the concrete class that's "under the hood" of a and returns it. Similarly for instanceof B -- it looks up the concrete class and thus the result of that expression is true.
a.toString() works similarly. At runtime, the JVM knows that the thing referenced by a is actually a B, so it executes B's toString method.
This is a fundamental property of class inheritance, interfaces, etc.
Class "A" does not have a variable "valB".
If you want to use the variable "valB" in class "B" either, you should first cast Class "A" to "B"
Try :
System.out.println(((B)a).valB);
You should know the difference between object type and instance type. First is determined at compile type and at runtime it's doing the best to keep that type safe. Instance type is a class which object is instantiated.
A a; //this is an object type
new B(); //this is an instance type
A a = new B(); //all together, but a is of type A, having instance of type B.

Why wouldn't polymorphism work in this case?

Object aThing = new Integer(25);
The method call aThing.intValue() is a compiler error. Why doesn't polymorphism work in this case?
Also there's a related statement in my textbook that is a bit convoluted:
The type of reference, not the type of the object referenced, determines that operations can be performed.
Can you briefly elaborate on that?
Where as Computer[] labComputers = new Computer[10]; works with polymorphism
public class Computer {
int ram;
public Computer(int rm){
ram= rm;
}
public String toString(){
String result = "ram is " + ram;
return result;
}
}
public class Notebook extends Computer{
int size;
public Notebook(int rm, int sz){
super(rm);
size = sz;
}
#Override
public String toString(){
String result = super.toString() + " size is " + size;
return result;
}
}
Added:
I believe somewhere in the middle, there would be
labComputer[1] = new Notebook(2,15);
labComputer[2] = new Computer(2);
For the method call labComputers[1].toString(), polymophism ensures that the correct toString is called. In my mind labComputer[1] = new Notebook(2,15); is equivalent to Object o = new Integer(25);. But polymorphism worked for my Computer example not in the Object-Integer example. Why?
It won't work because your variable is from Object class, so you can only use methods in the Object class. If you want to use it as an Integer, you should first do a (down) type casting:
Integer aInteger = (Integer)aThing;
//it could maybe work
aInteger.intValue();
Now, why could maybe work? Because downcasting could throw a ClassCastException if the type casting won't work.
Based in your example, I would post a basic code to show how polymophism works:
class Animal {
public void move() {
System.out.println("I'm an animal and I can move.");
}
}
class Cat extends Animal {
//this means a Cat would change the move behavior from the Animal instance
#Override
public void move() {
System.out.println("I'm a cat and I can move.");
}
}
class Dog extends Animal {
//this means a Cat would change the move behavior from the Animal instance
#Override
public void move() {
System.out.println("I'm a dog and I like to run.");
}
public void bark() {
System.out.println("I can bark!");
}
}
public class AnimalTest {
public static void main(String[] args) {
//it will take the behavior of the Animal class reference
Animal animal = new Animal();
//it will take the behavior of the Cat class reference
Animal cat = new Cat();
//it will take the behavior of the Dog class reference
Animal dog = new Dog();
//this will invoke the Animal#move method
animal.move();
//this will invoke the Cat#move method because it was overriden in the Cat class
cat.move();
//this will invoke the Dog#move method because it was overriden in the Dog class
dog.move();
//this line won't compile if uncommented because not all animals can bark.
//dog.bark();
//if you want to make the dog barks, then you should use the downcasting
((Dog)dog).bark();
//note that this will only work for Dog class reference, not for any other class
//uncomment this line and the code will compile but throw a ClassCastException
//((Dog)cat).bark();
}
}
Because even though Integer is an Object, Object is not an Integer and therefore it doesn't have Integer's methods, only Object's.
The type of reference, not the type of the object referenced, determines that operations can be performed.
By that they mean that even though the object that is referenced contains more functionality, if the type of the reference is different from the type of the object, then only the functionality of they type of the reference will be available.
In the following:
Object aThing = new Integer(25);
the type of aThing is declared as Object. Even though the implementation contains more than that, whatever else the implementation contains is not visible anymore, because the type of the variable is not Integer, but Object. Both Object and Integer have methods in common, but you can only access the ones provided by Object from now on, because nobody other than you know that this is really an Integer, not just an Object.
In the second example they mean that even though both Object and Integer have methods in common, when you call one of the methods, the method of the actual type will be called. So in this case, if you call toString() on aThing, you will call Integer's toString() method, not Object's. Therefore this is only an access issue. The fact that you declare it as an Object doesn't mean that you will get Object's methods to respond to calls, it only means that whatever methods that are present in Integer and not in Object will just be unavailable.
Example:
class Computer {
public int getInstalledOperatingSystems() { return 3; }
}
class Desktop extends Computer {
public String getShapeOfCase() { ... }
}
class Notebook extends Computer {
public int getInstalledOperatingSystems() { return 1; }
public String getBrand() { ... }
public void closeLid() { ... }
}
Now let's create a Notebook:
Notebook n = new Notebook();
Suppose that you have the following method:
public String showStatistics(Computer computer) {
result = "Operating systems: " + computer.getInstalledOperatingSystems();
return result;
}
If you call this method with the Notebook you defined above:
showStatistics(n);
then the method will receive the Notebook, because a Notebook is a Computer. It can call the Notebook's getInstalledOperatingSystems() method, because any Computer has that method. When it calls it, it will receive 1 as a result, because Notebook overrides Computer's implementation. But showStatistics() will not be able to call any other method that Notebook provides, because it doesn't know that it's being passed a Notebook. For all it cares, it has a Computer, and it doesn't know of any other method that a Computer doesn't have.
You can very well send it a Desktop instead, or maybe tomorrow you will create a GamingComputer that extends Desktop or a Server class that extends Computer. You can send that as well. But showStatistics() will not have access to any of Server's specific, specialized methods, because showStatistics() doesn't know that it's looking at a Server. It wasn't even invented when showStatistics() was written.
This is consistent with the statement that even though a Server is always a Computer, a Computer is not always a Server.
You can check though. So if you know that you might be passed in a Notebook, not only a computer, you can look for that and you can even call Notebook's methods, but you have to be sure that you're looking at a Notebook:
if (computer instanceof Notebook) {
// this is definitely a Notebook, so you can assure the compiler
// of that explicitly, by using a cast
Notebook notebook = (Notebook) computer;
result += "This isn't just a generic computer, it's actually a notebook! ";
// now that you have a variable of type Notebook, you can call
// whatever Notebook provides
result += "It's produced by: " + notebook.getBrand();
}
Think of it this way, a dog is an object and a dog class might have methods such as
dog.bark()
If you cast Dog up the hierarchy to an Object, it becomes less specific.
Object o = dog;
Now all you know is that o is an object, you do not know what kind of objects and therefore you cannot know if this object can bark.
When you move UP the hierarchy you almost always lose functionality by being less specific about what you have.
The compiler needs to be told what type it should expect the object to be, so it can look up the class and see if the method is legit.
So you can say Object aThing = new Integer(25); but then you'd want to say int thingValue = (Integer)aThing.intValue();
If you have a MySubClass object that is is a subclass of MyClass, and you want to call a method defined in MyClass (even if reimplemented in MySubClass) you could say:
Object myObject = new MySubClass();
int someValue = (MyClass)myObject.methodInMyObject();

When should or shouldn't I use a more general variable type for referencing my object?

There are 2 classes A and B, B extends A. What is the difference between
A a = new B();
and
B b = new B()?
Both create the object of class B. What is the difference?
You are right that in both cases an object of class B is created. The difference between the two declarations is in the type of the variable.
It is very important to keep the distinction between variables and objects in mind. For example, the following code defines 3 variables but only 2 objects:
Circle c1 = new Circle(5);
Circle c2 = c1;
Circle c3 = new Circle(5);
When you say
Shape s = new Circle(5);
instead of
Circle s = new Circle(5);
assuming Circle extends Shape then, even though in both cases you did create a circle object, in the former case you can only call shape methods on the circle (through the variable s) whereas in the second case you can you all circle methods (because you will be calling them through the circle variable c). That is a call like s.getArea() will work in both cases but something like s.getRadius() will ONLY be allowed in the second (unless you use an ugly cast).
So why do we often do things like the first case? That is, why do we often define our variables of a more general type than necessary? Usually we do this because we want to restrict the interface for safety. Perhaps we only care about shapes, but in this case the particular shape just happens to be a circle. If you cared about circle specific properties, then we would have used a circle variable. But we should strive to be as general as possible. Coding to the most general interface allows our code to work with shapes other than circles without modification.
Of course, for this to really sink in, you have to experience it firsthand, but hopefully this explanation is a start. There are many books and blog posts and articles that explain this in more detail with useful real-life anecdotes I'm sure.
A a = new B();
has only the attributes and methods of A.
B b = new B();
has the the attributes and methods of B.
If you added some attributes or methods to B, you can't call them with a.
The advantage is
Fruit f = new Mango();
Suppose
consumeFruit(Fruit f);
now you can call
consumeFruit(new Mango());
consumeFruit(new Strawberry());
Note:
For this case you would be only able to call the methods declared in the reference type. and object type's version will get invoked . and you would be only accessing fields from the reference type's class
See Also
Liskov substitution principle
If you say
List a = new ArrayList();
then you reference ArrayList only in one place in your code. That makes it easier to change it later to something else, like LinkedList;
Of course, this does not work if you need methods specific to ArrayList.
In general, you should use the most general type applicable.
This question is on Polymorphism. Following is an extract from Kathy Siera:
public class TestAnimals {
public static void main (String [] args) {
Animal a = new Animal();
Animal b = new Horse(); //Animal ref, but a Horse object
a.eat(); // Runs the Animal version of eat()
b.eat(); // Runs the Horse version of eat()
}
}
class Animal {
public void eat() {
System.out.println("Generic Animal Eating Generically");
}
}
class Horse extends Animal {
private void eat() { // whoa! - it's private!
System.out.println("Horse eating hay, oats, "
+ "and horse treats");
}
}
If this code compiled (which it doesn't), the following would fail at runtime:
Animal b = new Horse(); // Animal ref, but a Horse
// object , so far so good
b.eat(); // Meltdown at runtime!
Suppose this example:
We have class an animal:
public class Animal {
public void eat() {
// each animal can eat
}
}
Now we have another class dog:
public class Dog extends Animal {
public void bark() {
// dogs can bark
}
}
Now we can write this code:
Animal pet = new Dog();
Now we know, that pet can eat, but nothing more. But if we write
Dog pet = new Dog();
Then we know, that our pet can eat and bark.
Also there is safe and unsafe casting. Safe casting is from Dog to an Animal because each dog is animal (extends it)
Dog pet = new Dog();
Animal animal = pet;
But if we want to cast Animal to Dog we have to test if the instance of animal is really dog, because it doesn't have to be.
Animal pet = new Dog();
Dog myDog = null;
if (pet instanceof Dog) {
myDog = (Dog) pet;
}
Usually, declaring a parent class and assigning it an inherited class is useful when the parent class variable may be assigned different objects. For example
Pet p;
if (favoritePet == Pets.CAT) {
p = new Cat();
} else {
p = new Dog();
}
System.out.println(p.someMethodFromPet());

Categories