简体   繁体   中英

Java interface reference to concrete class which extends an abstract class

I'm trying to understand why Java works this way.

Let's say I have:

  1. Interface - IAnimal
  2. Abstract class - Mammal
  3. Concrete class - Dog

Mammal implements IAnimal and Dog extends Mammal .

Mammal declares and implements the method breastFeed() (of course Animal doesn't have this signature, since not every animal is a mammal)

Now I want to create a dog object and the interface will reference to this dog, this way:

IAnimal dog = new Dog();

Somewhere in run time, I might want to use the breastFeed method but the dog object wouldn't recognise this method.

One solution is to implement this method in the Dog class Other solution would be to make the reference to Dog from the Abstract class, which implement the interface, in this case, I get everything to the dog object.

I find these solutions messy and weird. Any thoughts? Maybe I missed something and there are some nicer and cleaner solution?

Thanks you, Yoav.

That's valid because of your inheritance chain. But the thing here is that dog is declared as IAnimal and it only exposes the methods which that interface has and not the ones that are in the implementation. If you need to access the breastFeed() method you should cast the value to the subclass.

Let's assume you have the interface IAnimal , two abstract classes Mammal and Bird and two concrete classes Dog and Eagle in a package called animals . Mammal and Bird implement the interface IAnimal while Dog extends Mammal and Eagle extends Bird . This situation would look like the following (thanks to @BackSlash for his comment):

Interface:

package animals;

public interface IAnimal {
    void feed();
}

Abstract classes:

Mammals:

package animals;

abstract class Mammal implements IAnimal {

    /*
     * This is the feeding method that every animal has to implement.
     * The trick here is to force the inconcrete method feed() to use the
     * mammal specific one that has to be implemented by concrete mammals
     */
    @Override
    public void feed() {
        breastFeed();
    }

    /*
     * This is how mammals feed,
     * so every concrete mammal has to implement this method (even humans...)
     */
    protected abstract void breastFeed();
}

Birds:

package animals;

public abstract class Bird implements IAnimal {

    /*
     * This time, force the inconcrete method feed() to use the
     * bird specific one that has to be implemented by concrete birds
     */
    @Override
    public void feed() {
        feedPrey();
    }

    /*
     * Birds feed prey to their young, most likely due to a lack of breasts
     */
    protected abstract void feedPrey();
}

Concrete classes:

package animals;

public class Dog extends Mammal {

    /*
     * This is how dogs feed, the implementation of how mammals feed
     */
    @Override
    protected void breastFeed() {
        System.out.println("currently breastfeeding... do not disturb!");
    }

}

You can check the output by some class with a main method like this:

import animals.Dog;
import animals.Eagle;
import animals.IAnimal;

public class Test {

    public static void main(String[] args) {
        IAnimal dog = new Dog();
        IAnimal eagle = new Eagle();
        dog.feed();
        eagle.feed();
    }

}

and the output would be what the dog does... (please excuse biological incorrectness, there might be birds that do not feed prey to their young. these examples are made for java, not for nature...)

You should totally forget the method breastFeed as this is just a specification. Just add a method feed() into IAnimal and then let the implementations decide how they eat:

interface IAnimal{
    void feed();
}

abstract class Mammal implements IAnimal{}

A Dog would probably eat some meat:

class Dog extends Mammal{
     public void feed(){
         System.out.println("Eating meat!");
     }
}

Whereas a BabyCat may drink some milk:

class BabyCat extends Mammal{
     public void feed(){
         System.out.println("Drinking milk!");
     }
}

This is the way to go, so when declaring a dog as follows:

IAnimal animal = new Dog();

You can then just call anmial.feed() . And let the implementations handle the rest.

I would introduce an interface IMammal that extends IAnimal .

interface IAnimal {
    public void feed();
}

interface IMammal extends IAnimal {
    public void breastFeed();
}

abstract class Mammal implements IMammal {

    @Override
    public void feed() {
        breastFeed();
    }

}

class Dog extends Mammal {

    @Override
    public void breastFeed() {
        System.out.println("Dog breastFeed()");
    }
}

You can then test for instanceof IMammal when needed.

I wouldn't use interfaces here as all the behaviour is inherited. However, using IAnimal dog = new Dog(); dog has access only to IAnimal methods. Hence if you were to store dog into a Mammal variable, you would then have access to the breastFeed() method.

For instance:

public abstract class Animal {
    public abstract void feed();
}

public abstract class Mammal extends Animal {
    public abstract void breastFeed();
}

public class Dog extends Mammal{

    @Override
    public void breastFeed() {
        System.out.println("Drinking milk!");
    }

    @Override
    public void feed() {
        System.out.println("Eating meating");
    }

}

public class TryingAnimal {

    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.breastFeed();
        dog.feed();

        Animal animal = dog;
        animal.feed();

        Mammal mammal = dog;
        mammal.breastFeed();
    }

}

Hope this helps.

You might want to declare your variable as Mammal , EG:

Mammal dog = new Dog();

OR you might want to cast your dog to Mammal or even Dog :

if (dog instanceof Mammal)
  ((Mammal) dog).breastFeed();

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