Ссылка интерфейса Java на конкретный класс, который расширяет абстрактный класс - PullRequest
0 голосов
/ 04 июля 2018

Я пытаюсь понять, почему Java работает таким образом.

Допустим, у меня есть:

  1. Интерфейс - IAnimal
  2. Абстрактный класс - Млекопитающее
  3. Конкретный класс - Собака

Mammal реализует IAnimal и Dog расширяет Mammal.

Mammal объявляет и реализует метод breastFeed() (конечно, Animal не имеет этой подписи, поскольку не каждое животное является млекопитающим)

Теперь я хочу создать объект собаки, и интерфейс будет ссылаться на эту собаку следующим образом:

IAnimal dog = new Dog();

Где-то во время выполнения я мог бы захотеть использовать метод breastFeed, но объект dog не распознает этот метод.

Одним из решений является реализация этого метода в классе Dog Другим решением было бы сделать ссылку на Dog из класса Abstract, который реализует интерфейс, в этом случае я получаю все для объекта dog.

Я нахожу эти решения грязными и странными. Какие-нибудь мысли? Может быть, я что-то пропустил, и есть более приятное и чистое решение?

Спасибо тебе, Йоав.

Ответы [ 5 ]

0 голосов
/ 04 июля 2018

Я бы представил интерфейс IMammal, который расширяет 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()");
    }
}

Затем вы можете при необходимости проверить instanceof IMammal.

0 голосов
/ 04 июля 2018

Вы должны полностью забыть метод breastFeed, так как это всего лишь спецификация. Просто добавьте метод feed() в IAnimal и позвольте реализациям решить, как они кушают:

interface IAnimal{
    void feed();
}

abstract class Mammal implements IAnimal{}

A Dog вероятно съест немного мяса:

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

Принимая во внимание, что BabyCat может пить немного молока:

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

Это путь, поэтому, когда вы объявляете собаку следующим образом:

IAnimal animal = new Dog();

Затем вы можете просто позвонить anmial.feed(). И пусть реализации справятся с остальным.

0 голосов
/ 04 июля 2018

Возможно, вы захотите объявить вашу переменную как Mammal, EG:

Mammal dog = new Dog();

ИЛИ вы можете разыграть dog на Mammal или даже Dog:

if (dog instanceof Mammal)
  ((Mammal) dog).breastFeed();
0 голосов
/ 04 июля 2018

Предположим, у вас есть интерфейс IAnimal, два абстрактных класса Mammal и Bird и два конкретных класса Dog и Eagle в пакете с именем animals. Mammal и Bird реализуют интерфейс IAnimal, в то время как Dog расширяет Mammal, а Eagle расширяет Bird. Эта ситуация будет выглядеть следующим образом (спасибо @BackSlash за его комментарий):

Интерфейс:

package animals;

public interface IAnimal {
    void feed();
}

Абстрактные классы:

Млекопитающие:

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();
}

Птицы

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();
}

Конкретные классы:

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!");
    }

}

Вы можете проверить вывод по некоторому классу с помощью метода main, например:

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();
    }

}

и результат будет такой, как собака (прошу прощения за биологическую неправильность, могут быть птицы, которые не кормят добычей своих детенышей. Эти примеры сделаны для явы, а не для природы ...)

0 голосов
/ 04 июля 2018

Это действительно из-за вашей цепочки наследования. Но дело в том, что dog объявлен как IAnimal, и он предоставляет только методы, которые есть в этом интерфейсе, а не те, которые есть в реализации. Если вам нужен доступ к методу breastFeed(), вы должны привести значение к подклассу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...