Реализация подтипов для методов интерфейса Java - PullRequest
3 голосов
/ 25 апреля 2019

Я изучал общие классы и методы в Java и сталкивался с этим вопросом в предыдущей статье

enter image description here

Я попытался реализовать интерфейс и классы, предложенные в вопросе, включая метод заправки, и не нашел проблем с передачей аргумента Car в качестве параметра

Моторизованный интерфейс

public interface Motorized {

    public int getFuel();

}

Класс автомобиля

public abstract class Vehicle{

    private int fuel;

    public Vehicle(int fuel) {

        this.fuel = fuel;
    }

    public String toString(){

        return("Fuel is: " + getFuel());
    }

    public int getFuel() {

        return fuel;
    }
}

Класс автомобиля

public class Car extends Vehicle implements Motorized {

    int seats;

    public Car(int fuel, int seats) {

        super(fuel);
        this.seats = seats;
    }

    public int getSeats() {

        return seats;
    }

    @Override
    public String toString(){

        return("Fuel is: " + getFuel() + "and the car has" + getSeats() + "seats.");
    }
}

метод испытания

public class VehicleTest {

    public static Motorized refuel(Motorized v) {

        return v;
    }

    public static void main(String[] args) {

        Car car = new Car(15, 5);

        System.out.println(refuel(car));
    }
}

Может кто-нибудь объяснить мне, в чем проблема с этим вопросом, и почему моя реализация не отражает эту проблему?

Ответы [ 3 ]

2 голосов
/ 25 апреля 2019

Проблема заключается в возвращаемом значении метода:

public static Motorized refuel(Motorized v)

Вы сказали, что у вас не было проблем с передачей Car, и это полностью допустимое утверждение.Но вы не пытались вернуть значение из метода refuel:

Car car = ...
Car refueled = refuel(car); // compiler error: Motorized is not assignable to Car!

Тип возвращаемого значения - Motorized, хотя Car extends Motorized, вы не можете быть уверены, что экземпляр Motorizedто, что возвращается, будет всегда быть Car.Посмотрите этот упрощенный пример:

public static Motorized refuel(Motorized v) {
    // try to refuel
    // uh oh... the gas station exploded
    // you have to change to a motorbike which just stands around
    return new Motorbike();
}

Теперь вы можете ожидать Car, но вы получите Motorbike, поэтому даже сбой приведения:

Car refueled = (Car) refuel(car); // ClassCastException, Motorbike is not a Car

Вы можете сделать это схотя дженерики:

public static <M extends Motorized> M refuel(M m) {
    // refuel the motorized vehicle
    return m;
}

Хотя, если бензоколонка снова взрывается, у метода есть проблема.Он не знает, что на самом деле M.Так что это может спасти вас от головной боли.

1 голос
/ 25 апреля 2019

вместо

System.out.println(refuel(car));

попробовать

Car refueledCar = refuel(car);

Вы обнаружите, что он не компилируется, потому что переменная, которую вы возвращаете из метода refuel, не является экземпляром Car, а скорее является своего рода объектом (но компилятор не ' * не знает ( вид объекта), который реализует Motorized. Вы можете привести к 1014 * с

Car refueledCar = (Car) refuel(car);

Но это довольно неуклюже и делает ваш код более склонным к ошибкам. К счастью, есть более эффективные способы решения этой проблемы. В частности, с использованием дженериков. Если вы измените подпись метода для refuel на эту:

public static <T extends Motorized> T refuel(T t)

вы обнаружите, что тип, который вы возвращаете, как известно, совпадает с типом, который вы передаете, и компилятор совершенно доволен строкой выше, которую ранее не удалось скомпилировать.

В порядке пояснения, эта сигнатура нового метода в основном означает «Существует некоторый неизвестный тип, T, который расширяет Motorized. Метод принимает переменную типа T и имеет возвращаемое значение того же типа. "

0 голосов
/ 25 апреля 2019

Для этого вы можете использовать универсальный метод .

Вот ультра-упрощенная иерархия (Vehicle здесь не учитывается).

interface Motorized {
    void refuel();
}
class Car implements Motorized {
    @Override
    public void refuel() {
        // TODO
    }
}

Теперь вот ваш тестовый класс:

class VehicleTest {
//         | generic method type parameter bound up to any `Motorized`
//         |                     | returns same type...
//         |                     |        | ... as given type
//         |                     |        |
    static <T extends Motorized> T refuel(T t) {
        t.refuel();
        return t;
    }

    // and here's some usage
    public static void main(String[] args) {
        Car car = new Car();
        Car refueled = refuel(car);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...