This question already has answers here:
Downcasting in Java
(12 answers)
Closed 8 years ago.
I understand that upcasting/downcasting in objects do not change the object, but rather change how we deal with them. ( or change the type of their reference if I understand correctly).
However, if we have an Animal class that has a getName(); method, and a dog class which is a child class of Animal.
Animal Billy = new Animal("billy",1);
Dog b = (Dog)Billy;
b.getName();
I understand that it will compile, but give a run time error, my question is why, getName(); method is present in the Animal Class, and so it is inherited by the Dog class, so it's present in both, what prevents us from having it done?
Whenever you downcast check it first using instanceof operator.
Animal billy = new Animal("billy",1);
if (billy instanceof Dog) {
Dog b = (Dog) billy;
b.getName();
}
since Animal can be any thing Cat, Dog etc. It's not known at compile time hence before downcast you have to check it.
getName() method is present in the Animal Class, and so it is inherited by the Dog class, so it's present in both, what prevents us from having it done?
Nothing prevent you but you have to do as shown below.
If getName() method is present in Animal class then there is no need to downcast just call it on the reference of Animal class and the actual getName() method of Dog class will be called.
Animal billy = new Dog("billy",1);
billy.getName();
Read more about Overriding and Hiding Methods
Related
I have a basic doubt in polymorphism in Java. I have written the code below in one file named AnimalTestDrive.java. According to me the code below should work specially the line in bold but unfortunately its not. Can you please explain why, I have given the error below:
class Dog extends Animal {
public void dogMethod() {
System.out.println("In Dog method");
}
}
public class AnimalTestDrive {
public static void main(String args[]) {
Dog d = new Dog();
d.dogMethod();
d.animalMethod();
Animal animal = new Animal();
animal.animalMethod();
animal = d;
**animal.dogMethod(); // THIS IS NOT WORKING**
}
}
Let's try to look at this line the same way that the compiler would:
animal.dogMethod();
First, it needs to work out what animal means. That's nice and easy - it's a local variable in the current method, so it doesn't need to look far.
The compile-time type of that variable is Animal. The compiler doesn't care what the value of the variable will be at execution time - it only uses the information about the declared type.
So, that's what it uses to try to look up what dogMethod() means within the context of animal, i.e. with type Animal. First it looks in Animal, then in java.lang.Object (the implicit superclass of Animal) - but neither of those classes contains a declaration of dogMethod. At that point, the compiler has to give up with an error - it can't find the method. It doesn't matter that the method is available on the execution-time type of the object that the value that animal refers to. It has to bind it at compile-time, using only the information available at compile time.
The only decision made at execution time is which implementation of a method is used - for example, if you called animal.toString() and the Dog class had an override, e.g.
#Override public String toString() {
return "I'm a dog";
}
then the compiler would find the toString() method from java.lang.Object, so it would know that the method call was valid - but the implementation in Dog would be used because of the execution-time type of the object.
As far as Java can tell, animal is just an animal, so it can only do the things defined in your Animal class. If you want to be able to use the Dog method, and you know your animal is a Dog, you have to cast it to a Dog for the method to be visible.
In other words, the only methods and fields available for a variable are the ones defined by it's left hand side type. You can either say ((Dog)animal).dogMethod(); to refer to animal as a Dog, or create a new variable Dog animalAsDog = animal; and call your method on animalAsDog.
I believe this question needs some illustration.
So, here is a sample code.
// Animal - abstract class (Parent class)
// Dog - inherited class (Sub class)
Animal puppy = new Dog(); // creating an instance.
So my question is, how is using both the class names when instantiating an object is right?
My pardon If this comes as a silly question. Can someone explain the breakdown of this weird instantiation?
new Dog() creates a new instance of Dog and returns a reference to that Dog
Animal puppy creates a variable (a named thing that can hold a reference to an object) that is capable of referring to any object that is an Animal
= initializes the variable on the left to the value on the right. Since a Dog is an Animal, a reference to a Dog is a reference to an Animal, and therefore the initialization is legal.
I've just started learning object oriented programming from the book head first java.It said that polymorphism enables me to create an array of the superclass type and then have all the subclasses as the array elements.But when I tried writing code using the same principles it ran into error saying
error: cannot find symbol
I made the classes the superclass was animal and the dog class extended the animal class having a fetch method of its own, but when I referenced the dog variable as animal it did not work here is the code
The Animal class:
public class animal{
String family;
String name;
public void eat() {
System.out.println("Ghap Ghap");
}
public void roam() {
System.out.println("paw paw");
}
}
The dog class:
public class dog extends animal {
public void fetch() {
System.out.println("Auoooooooo");
}
}
The Tester class:
public class tester {
public static void main(String args[]){
animal doggie = new dog();
doggie.fetch();
doggie.eat();
doggie.roam();
}
}
The error:
tester.java:4: error: cannot find symbol
doggie.fetch();
^
symbol: method fetch()
location: variable doggie of type animal
1 error
Edit: Last time I asked this question I went home thinking the object doggie is of the type animal and it has no idea of about the fetch() function that has been declared in the dog class. But adding the line
System.out.println(doggie.getClass().getName());
Gives dog as the type of the class, if dog is indeed the type of the class, shouldn't it have the knowledge of the method declared within it
?
When using polymorphism, if you create an instance of the subclass and store its reference in a variable of superclass type, you can only call those methods on the newly created instance which are present in the super class.
In your code, you created an instance of dog class and stored its reference in doggie which is of type animal (super class of dog), In such case, you can't call any method on dog class instance that isn't available in animal class.
fetch method is not defined in the animal class hence you get the error.
Solution
Either define the fetch method in the animal class
OR
change
animal doggie = new dog();
to
dog doggie = new dog();
Since the fetch() method doesn't exist in animal, its throwing the error.
You can define a fetch method in animal and override it in dog class.
You are referencing doggie.fetch() but this is not a method defined in animal.
Since you are using your doggie object as an animal you can not use this method.
If you would like to use the method, you can do something like an instance check:
if(doggie instanceOf dog){
((dog)doggie).fetch();
}
If you really want to understand the depth of this concept, you should understand the Liskov Substitution Principle, which, in a brief, is described as follows:
In a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., an object of type T may be substituted with any object of a subtype S) without altering any of the desirable properties of the program.
Central idea behind this concept is to NOT break the contract "signed" with the parent type (that is, extending a class, or implementing an interface).
If you were able to invoke any method, available in your subtype, on the reference stored in its parent type, disregarding the contract between your subtype and its super type(s), you may have an unintended malfunction - runtime exceptions, to be more precise.
Last but not least: Please follow the Java naming conventions and name your classes with the Pascal Case convention. This is very important.
I have seen other answers to similar questions but all of them rely on the fact that the language is defined to be like this. Following is what I am seeking an explanation to:
In an inheritance hierarchy, the parent types can hold child objects implicitly (why?), however for the child references to hold a parent object, explicit downcast is necessary (why?).
Please cite some example that explains why not doing this will fail, I mean using Animal, Dog type etc. If this question is already answered and I have missed it, citing that also will be helpful.
For example:
class Animal{
public void eat(){};
}
class Dog extends Animal{
public void bark(){};
}
class App{
Animal an = new Dog(); //1. why can animal store a dog but not a dog store an animal
Dog dog = (Dog) new Animal(); //2. Dog has the behavior of an Animal so why explicit downcast
}
I just want to know how this lines make sense, other than just knowing they are language semantics. Better if your answer looks like that as a granny explaining this to her grandchild.
Edit:
I just was wondering that Dog inherits Animal and has all the behavior. Hence number 2 above should have been allowed without explicit downcasting.
Or, that (I think I got it now) when I ask a Dog to store an Animal there are possibilities that I actually get a Cow or a Horse, because Animal being parent can hold any of its subtypes. If that's the case then why has Java allowed Animal to hold subtypes since there might be behavior typical to subtypes like a Dog will bark(), for that again compiler has to check and report. I know the rules just trying to reason out in the simplest of sense.
The gain of strict type binding in Java is that you get compile time errors, instead of runtime errors, when possible.
Example:
class Animal {
void eat() {}
}
class Dog extends Animal {
void bark() {}
}
class Pigeon extends Animal {
void pick() {}
}
class MyFunction {
void run() {
Animal first = new Pigeon();
// the following line will compile, but not run
((Dog)first).bark();
}
}
If you have such code in a simple example like this, you will spot the problem at once. But consider having such a problem in a project, in a seldom called function at the depth of thousands of lines of code, in hundreds of classes. One day in production, the code fails and your customer is upset. And its up to you to find out why it failed, what happened and how to fix it. Its a horrible task.
So, with this somewhat complicated notation Java nudges you into thinking again about your code, the next example would be how its done better:
class MyFunction {
void run() {
Pigeon first = new Pigeon();
// the following line will NOT compile
first.bark();
// and neither will this. Because a Pigeon is not a Dog.
((Dog)first).bark();
}
}
Now you see your problem at once. This code, will not run. And you can avoid the problems ahead by using it correctly.
If you make your Animal class abstract (which you should), you will see that you can only instantiate specific animals, but not general ones. After that you will start using the specific ones when required and be relieved that you can reuse some code when using the general class.
Background
Conceptually, runtime errors are harder to find and debug, then compile time errors. Like, seriously hard. (Search for NullPointerException here on Stack Overflow and you will see hundreds of people who struggle to fix runtime exceptions)
In a Hierarchy of things (in general, not programming related) you can have something general "Thats an animal". You can also have something specific "Thats a dog". When someone talks about the general thing, you can't expect to know the specific thing. An animal can't bark up a tree, because birds could not, neither do cats.
So, in Java in particular the original programmers found it wise to decide that you need to know an object specific enough to call the functions of that object. This ensures that if you didn't pay attention, the compiler will warn you, instead of your runtime.
Your particular case
You assume that:
Dog dog = (Dog) new Animal();
should work, because a Dog is an Animal. But it won't, because not all Animals are Dogs.
BUT:
Animal an = new Dog();
works, because all Dogs are Animals. And in this specific case
Animal an = new Dog();
Dog dog = (Dog)an;
will work too, because the specific runtime state of that animal happens to be Dog. Now if you change that to
Animal an = new Pigeon();
Dog dog = (Dog)an;
it still WILL compile, but it WILL NOT run, because the second line Dog dog = (Dog)an; fails. You can't cast a Pigeon to a Dog.
So, in this case you WILL get a ClassCastException. Same if you try to cast new Animal() to Dog. An Animal is NOT a Dog. Now, that will happen at runtime and that is bad. In the Java way of thinking, compile time errors are better then runtime errors.
Yes. One simple real time example to understand the theory is
Every Truck driver is Driver but not you cannot say every Driver is a Truck Driver.
Where
Driver -Parent
Truck Driver - Child.
Animal an = new Dog(); //1. why can animal store a dog but not a dog store an animal
You ordered for an Animal and the shop keeper gave a Dog for you and you are happy since Dog is an Animal.
Dog do = (Dog) new Animal(); //2. Dog has the behavior of an Animal so why explicit downcast
You are asking for a Dog and the shop keeper gave an Animal to you. So it is so obvious that you check that the Animal is a Dog or not. Isn't you ?? Or you just assume that you got a Dog ?
Think.
In java references are used. Suppose we have below classes
class A{
Integer a1;
public A(){
// a1 is initialized
}
public void baseFeature(){
super.baseFeature();
// some extra code
}
}
and
class B extends A{
Integer b1;
Integer b2;
public B(){
super();
// b1 , b2 are initialized
}
#Override
public void baseFeature(){
super.baseFeature();
// some extra code
}
public void extraFeature(){
// some new features not in base class
}
}
Below all 3 statements are valid
A a = new A();
B b = new B();
A b1 = new B();
In java references are used to refer to the objects kept in Heap.
A reference of type A should not be considered as if it can not hold the objects which have more memory required than an object of class A. Its the references and not the objects holders.
In case of sub type object creation, Constructor call follows : Parent-constructor call followed by constructor call of the actual class whose object is being created.
Sub class object can be said to be having features at-least as much as the Parent type has.
A b = new B() has no confusion, as object of B has all the features of its parent.
Sub class object has all the features as defined in its parent, so any parent class method can be called on the object of sub class
Sub classes can have much more features, which parent class does not have, so calling a method of sub class on object of parent will lead to problems.
Suppose In Java B a = new A() is valid then if a.extraFeature() is invoked then it is obvious of an error.
This is prevented by compile time error. If in case downcasting is needed, then the programmer must do it with extra care. This is the intention of compile time error. Below cases down-casting would not lead to issues but the onus is on the programmer to see if situation is of this kind or not.
public void acceptChild(Child c){
c.extraMethodNotWithParent();
}
Parent p = new Child();
acceptChild((Child)p);
Here programmer is given compile time warning if down-casting is not done. Programmer can have a look and can see if the actual object is really of sub class type then he/she can do explicit down-casting. Thus issues will only come if the programmer has not taken care.
All the replies here help. I was confused.
Removing all the chains in my brain, if I simply think, it now looks like:
Parent can hold subtypes.
You mean thats the benefit of polymorphism and how its achieved.
Child types can hold parent but need explicit downcast.
Since polymorphism is allowed, this downcast is a safeguard. The compiler is asked to trust that the Animal in code is actually going to be a Dog instance that the Dog type wants to hold.
This is because inheritance is specialization, when you inherit a class T, the child class S is a specialization of T, for that Java don't need a cast, but T can have many childs, S' inherits T, S' too, so here if you have an object of S and its referenced as T and you want to get your origin type again, you should make a cast to S and Java check the type at runtime.
an example :
A man and a woman are humans, but a human is a man or woman.
Man man = new Man();
Human human = man //Legal.
Man man2 = (Man) humain; //Legal
Woman human = (Woman) humain; //Error, Java can't cast the humain to Woman, because its declared originaly as Man.
If Java is sure made the cast implecitly, if not, it reports it and don't decide in your place, This check is made at runtime.
This question already has answers here:
Calling overloaded inherited methods using super class reference
(10 answers)
Closed 6 years ago.
I'm currently working through a Java Book and i've started reading about inheritance and polymorphism.
I'm making a test program that stores animal information, and I want to overload the method that sets the animals size. The user knows the sizes of the dog, but no other animals, so sets animal size to 0 if it's not a dog, and for the dog, it uses its own setSize method with parameters.
However i've tried two methods of creating a dog, by creating an animal object and also by creating a dog object. I assumed that even though the testDog1 variable is of type Animal, it would still be able to overload methods from the Dog class as it is a dog object.
Could anyone please explain why testDog1 does not work but testDog2 does work?
public class Loader {
public static void main(String[] args){
int dogSize = 100;
Animal testDog1 = new Dog();
testDog1.setSize(dogSize);
Dog testDog2 = new Dog();
testDog2.setSize(dogSize);
}
}
public class Animal {
public int size;
public void setSize(){
size = 0;
System.out.println(size);
}
}
public class Dog extends Animal {
public void setSize(int dogSize){
size = dogSize;
System.out.println(size);
}
}
If you are going to refer to testDog1 in your code, the compiler will consider it to be of class Animal. Meaning you can reassign it with an instance of Cat or Bird or anything else, where setSize(int) doesn't exist.
After testDog1 was declared of type Animal, you need to comply with the contract of that class.
The fact that it is currently a Dog doesn't make the specific methods of Dog visible. You will probably learn about casting in the next chapter of the book, though.
Animal testDog1 = new Dog();
An instance of Dog would be created and assigned to the testDog1 variable only when you run the program. ie.) Runtime.
Compiler doesn't run the program or make hard and fast assumptions of what the nature of each statements or variables would be while checking for correctness.Had that been the case , the following stmt would have been a compile time error too.
Animal testDog = null;
testDog.setSize(); // How can you call setSize() on null right? for the same reason mentioned above.
At compile time, all that the compiler knows is that in testDog1 you will have a reference to an Animal. Since an Animal does not have a setSize(int), the compiler cannot locate it and it fails.
It is important to know which operations are done at compile time (the compiler knows the type of the variable) and which one are done at runtime (the runtime knows the actual instance of the object).
Your testDog1 does not find setSize(int dogSize) method in Animal Class Hence it is a compilation error.
where as testDog2 find setSize(int dogSize) method in Dog class and it will compile.
testDog1 is an Animal object. Without casting it as a Dog, it doesn't know that setSize(int) exists.
(Dog)testDog1.setSize(dogSize) tells the compiler that it's actually a Dog.