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.
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.
While revising for the Java SE 8 Programmer I (formerly OCA) Certification exam, I came across the following statement:
"A reference to an interface requires an explicit cast to be assigned
to a reference of any class, even one that implements the interface.
An interface reference requires an explicit cast to be assigned to a
class reference."
I think this is slightly inaccurate as it is possible to assign a reference to an interface to a reference of the class Object without an explicit cast.
interface Animal {}
public class Dog implements Animal {
public static void main(String[] args) {
Animal animal = new Dog();
Dog dog = animal; // Doesn't compile - requires explicit cast to Dog
Object o = animal; // Compiles
}
}
Is this due to a link between the Object class and interfaces akin to that described in this answer relating to the class file format?
From this answer relating to accessing the Java Object class methods using an interface reference, I've summarized that:
Using an interface reference, you can only access methods defined in
the interface but not class specific methods.
Interfaces have "Hidden Declared" methods of class Object.
You can access methods of the Object class via any interface reference because, although an interface
doesn't extend from the Object class, every root interface in Java has implicit declarations of
methods corresponding to each method in the Object class.
JLS §9.2 - Interface members:
If an interface has no direct superinterfaces, then the interface
implicitly declares a public abstract member method m with signature
s, return type r, and throws clause t corresponding to each public
instance method m with signature s, return type r, and throws clause t
declared in Object, unless a method with the same signature, same
return type, and a compatible throws clause is explicitly declared by
the interface.
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.
Can a subclass also be a superclass of another subclass in Java? Perhaps this is not the best example, but consider the following classes:
public class Animal { }
public class Dog extends Animal { }
public class Cat extends Animal { }
public class Siamese extends Cat { }
public class JackRussel extends Dog { }
Does inheritance allow for this sort of behaviour?
Given that JackRussels would require the methods and properties of both an Animal and a Dog, and Siamese's would require the methods and properties of both Animal and Cat.
If not, is there a generalised approach I could take to achieve this sort of behaviour?
Cheers
Yes, that behavior is exactly what is expected when using inheritance in Java.
Here's some quick reading that you may find usefull: http://www.homeandlearn.co.uk/java/java_inheritance.html
Your JackRussel object will inherit all fields and methods from it's Animal and Dog super-classes that are:
not declared private;
are not overridden (in which case will get only have access to the overridden one);
are not shadowed (in which case will get only have access to the shadowed one);
Here's another quick link on shadowing and overriding in Java:
http://docstore.mik.ua/orelly/java-ent/jnut/ch03_04.htm
Having those point in mind, you can easily design inheritance tree that can propagate parent’s behavior and state to all of its children.
Thanks.
Of course this is possible. But saying that the Siamese would require methods and fields from both superclasses is a bit wrong. When cat extends animal it gets all of the fields and methods of animal (if you do not override them). Then, when Siamese extends that cat class, it will automatically get the whole Cat class, including the things that are from the Animal class, regardless of whether they are overriden or not.
In short terms, this is possible.
Here is the scenario:-
class Canine{
public void roam(){
System.out.println("Canine-Roam");
}
}
public interface Pet{
public abstract void roam();
}
class Dog extends Canine implements Pet{
public void roam(){
System.out.println("Dog Roam");
}
public static void main(String [] args){
Dog adog = new Dog();
adog.roam();
}
}
I am aware that JVM must not have any confusion in choosing which method to run, that means, which method gets over-ridden. But I am confused anyway. Why does this program compile?
No - the same method cannot exist in a class twice.
An interface simply declares a requirement for a class to implement a particular method. It does not actually create that method.
So a class that acquires a method implemention through inheritance has that method defined. This (single) implementation satisfies the interface's requirements.
In your case:
Dog extends Canine, so it means that the roam() method from Canine is available, and would be exposed as a method on Dog objects if not overridden.
But then Dog then overrides the superclass' method with its own definition of roam(). This is allowed, and there is still just one unambiguous method called roam() on Dog - the new override.
Dog implements Pet, which means it is required to have a roam() method. It does - so it's a valid implementation of this interface.
Your case is totally fine and it will run fine and outputs Dog Roam. That means the function in Dog class runs.
You did not get any compile time errors because the method in Dog is implementing the abstract method declared in interface and coincidentally the method signature of this method matches with the parent class.
I think you confuse two things:
Implementation: what is done? see: Canine.roam() and Dog.roam()
Interface: how do you invoke it? see: Pet.roam()
It's clear that you have two "implementations" of roam() in two classes:
Canine.roam()
Dog.roam()
There are never two same methods in same class.
And because Dog extends Canine, the method Canine.roam() got overriden. Your main() function uses Dog.roam() instead.
You had created object for Dog type and assigned it to Dog reference type.
Overridding, has nothing to do here. Though you extended Dog class from canine, both are 2 different types.
At compile time, the compiler checks whether the method definition is present in Reference type. Thats it. And it has to be present there.
At run time, JVM checks for the method in reference type first, then it finds is there any over ridden version for the same method present in "Reference type's " subclass.
If present, it will be executed. Or else, Reference type's method will be executed.
i.e.
Canine c=new Dog();
will execute Dog's method.