简体   繁体   中英

Is it possible to call subclasses' methods on a superclass object?

Animal is a superclass of Dog and Dog has a method called bark

public void bark()
{
    System.out.println("woof");
}

Consider the following:

Animal a = new Dog();
if (a instanceof Dog){
    a.bark();
}

What will happen?

  1. the assignment isn't allowed
  2. the call to bark is allowed and "woof" is printed at run time
  3. the call to bark is allowed but nothing is printed
  4. the call to bark causes a compile time error
  5. the call to bark results in a run time error

I said 2 as we are checking if the object is a dog; as dog is the class with the bark method in it, if it is then we call it which will print out :s

Is my understanding correct here?

This won't compile since Animal does not have a method called bark. Think of it this way, all dogs are animals, but not all animals are dogs. All dogs bark, but not all animals bark.

no - the answer is;

4) the call to bark causes a compile time error

the bark method isnt defined as a method on the assigned type Animal, which will therefore result in compile time issue; this could be solved by casting;

((Dog)a).bark();

The key is in the following line:

Animal a = new Dog();

Although a new instance of Dog was created, its reference is by a which is declared to be of the type Animal . Therefore, any references to a makes the new Dog be handled as an Animal .

Therefore, unless Animal has a bark method, the following line will cause a compiler error:

a.bark();

Even though a is tested to see if it is an instance of Dog , and a instanceof Dog will actually return true , the variable a is still of is of type Animal , so the block inside the if statement still handles a as an Animal .

This is a feature of statically-typed languages where variables are assigned a type ahead of time, and checked at compile-time to see that the types match. If this code were performed on a dynamically-typed language , where the types are checked at runtime, something like the following could be allowed:

var a = new Dog();
if (a instanceof Dog)
    a.bark();

a.bark() is guaranteed only to execute when the instance is a Dog , so the call to bark will always work. However, Java is a statically-typed language, so this type of code is not allowed.

In Head First Java they use the very good analogy of a TV remote control for a reference and your TV as the object that the reference points to. If your remote only has buttons (methods) for on, off, channel up and down, and volume up and down, it doesn't matter what cool features your TV has. You can still only do those few basic things from your remote. You can't mute your TV, for example, if your remote has no mute button.

The Animal reference only knows about Animal methods. It doesn't matter what other methods the underlying object has, you can't access them from an Animal reference.

If the idea is to print the subclass method from superclass object, this will work:

Instead of Animal a = new Dog(); if (a instanceof Dog){ a.bark(); } Animal a = new Dog(); if (a instanceof Dog){ a.bark(); } Animal a = new Dog(); if (a instanceof Dog){ a.bark(); } change to

Animal a = new Dog();

if (a instanceof Dog){ 
    Dog d = (Dog) a; 
    d.bark();
}  

This casts the superclass back to subclass and prints it. although its bad design, its one way to know which child class object its pointing to dynamically.

It's 4. You can't ask a generic Animal - which is what your code says a is - to bark. Because you could just as easily have said

Animal a = new Cat();

and the bark line doesn't have a way to know that you didn't.

In java(only language i know) you can create an empty method and call it in super class. Then you can override it in subclass to do whatever you want. This way the super class calls its subclass' method.

public class Test {
    public static void main(String[] args){
        Snake S = new Snake();
        Dog d = new Dog();
    }
}


class Animal{ //Super Class
    public Animal(){
        bark(); //calls bark when a new animal is created
    }
    public void bark(){
        System.out.println("this animal can't bark");
    }
}



class Dog extends Animal{ //Subclass 1
    @Override
    public void bark(){
        System.out.println("Woof");
    }
}



class Snake extends Animal{//Subclass 2
    public void tss(){
    }
}

This code calls an object of Snake then calls an object of Dog. It writes this to console:

this animal can't bark
Woof

Snake doesn't have any bark method so super class' method is called. It writes the first line to the console. Dog has a bark method so super class calls it instead. It writes the second line to the console.

FYI, this is not a good design.

Just about any time you have code of this form:

if (x instanceof SomeClass)
{
   x.SomeMethod();
}

you are abusing the type system. This is not the way to use classes, it's not the way to write maintainable object oriented code. It's brittle. It's convoluted. It's bad.

You can create template methods in a base class, but they have to call methods that exist in the base class and are overridden in sub-classes.

"I said 2 as we are checking if the object is a dog; as dog is the class with the bark method in it, if it is then we call it which will print out :s"

Your rationale is correct, but that's not the way it works.

Java is an static typed language that means, the validity of the methods an object may respond to is verified at compile time .

You may think the check:

if( a instanceof Dog ) 

Would do, but actually it doesn't. What the compiler do is check against the "interface" of the declared type ( Animal in this case ). The "interface" is composed of the methods declared on the Animal class.

If the bark() method is not defined in the super class Animal the compiler says: "Hey, that won't work".

This is helpful, because "sometimes" we make typos while coding ( typing barck() instead for instance )

If the compiler doesn't not warn us about this, you would have to find it at "runtime" and not always with a clear message ( for instance javascript in IE says something like "unexpected object" )

Still, static typed language like java allow us to force the call. In this case it is using the "cast" operator ()

Like this

1. Animal a = new Dog();
2.  if (a instanceof Dog){
3.     Dog imADog = ( Dog ) a;
4.     imADog.bark();
5. }

In line 3 your are "casting" to a Dog type so the compiler may check if bark is a valid message.

This is an instruction to to compiler saying "Hey I'm the programmer here, I know what I'm doing". And the compiler, checks, Ok, dog, can receive the message bark(), proceed. Yet, if in runtime the animal is not a dog, a runtime exception will raise.

The cast could also be abbreviated like:

if( a instanceof Dog ) {
   ((Dog)a).bark();  
}

That will run.

So, the correct answer is 4: " the call to bark causes a compile time error "

I hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM