Сделайте, чтобы переопределенный метод возвратил другой тип - PullRequest
3 голосов
/ 07 февраля 2012

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

public class Test { 

    public static void main(String[] args) {
        PetrolCar myCar = new PetrolCar();
        String mileage = myCar.getEngine().getMileage();

    }
}

class Engine {
    protected String var = "Engine";

    protected String getVar() {
        return this.var;
    }
}

class PetrolEngine extends Engine {
    protected String var = "PetrolEngine";
    protected String mileage = "0";

    PetrolEngine() {
        super();
        mileage = "100";
    }

    protected String getVar() {
        return this.var;
    }

    protected String getMileage() {
        return mileage;
    }
}

class Car {
    protected Engine engine;

    protected Engine getEngine() {
        return engine;
    }
}

class PetrolCar extends Car {
    protected PetrolEngine engine;
}

Очевидно, myCar.getEngine().getMileage() не будет работать, потому что getEngine() вернет экземпляр Engine, а не экземпляр PetrolEngine. Какой обходной путь для этого? Я не хочу переопределять getEngine() во всех подтипах Car, в то же время я хотел бы получить более конкретный тип PetrolEngine при вызове getEngine() в экземпляре PetrolCar, без необходимость набрать приведение. Это возможно?

Другими словами, есть ли способ связать Engine из PetrolCar с PetrolEngine? В приведенном выше случае он создает две отдельные переменные экземпляра engine, одну из которых PetrolEngine типа внутри PetrolCar, а другую Engine, к которой я могу получить доступ как super.getEngine() изнутри PetrolCar. Я не хочу разных переменных. Я хотел бы, чтобы код знал, что "Engine" из "PetrolCar" является PetrolEngine.

РЕДАКТИРОВАТЬ: Изменен MyCar на PetrolCar, чтобы избежать путаницы, которая заставляет всех поверить, что MyCar должен быть экземпляром Car, а не подтипом.

Ответы [ 9 ]

2 голосов
/ 07 февраля 2012

Да, вы можете.Они называют это ковариантным возвратом (начиная с Java 1.5):

public class Test {

    public static void main(String[] args) {

        MyCar car = ...;
        car.getEngine().getMileage();
    }
}

interface Car {
    Engine getEngine();
}

interface MyCar extends Car {
    PetrolEngine getEngine();
}

interface Engine {
}

interface PetrolEngine extends Engine {
    String getMileage();
}
2 голосов
/ 07 февраля 2012

Существует более одного способа кожи кролика.

Самое главное, методы get (и set) обычно указывают на неинтересные объекты. Гораздо лучше поместить поведение в объект, чем запускать процедурный скрипт снаружи.

Метод может подходить для всех двигателей, поэтому его можно переместить в базовый класс.

Вы можете создать подкласс Car для различных типов двигателей

Car можно сделать родовым class Car<E extends Engine> {.

В необычных случаях может подойти маленький посетитель. Добавьте метод к Engine, public void accept(EngineVisitor visitor), где interface EngineVisitor { void visit(Engine engine); void visit(PetrolEngine engine); }.

(Пара других комментариев: как правило, хорошая идея, чтобы класс был либо абстрактным, либо конечным - расширение конкретных классов, как правило, является плохим. Наследование реализации, как правило, следует использовать экономно, и protected действительно очень редко. Если вы собираетесь поля, которые нельзя изменить, отметьте их final.)

2 голосов
/ 07 февраля 2012

Я предлагаю вам превратить Engine в интерфейс:

public class Test {
    public static void main(String[] args) {
        MyCar myCar = new MyCar();
        String mileage = myCar.getEngine().getMileage();
        System.out.println("The mileage is " + mileage);
    }
}

interface Engine {
    String getVar();
    String getMileage();
}

class PetrolEngine implements Engine {
    protected String var = "PetrolEngine";
    protected String mileage = "0";
    PetrolEngine() { mileage = "100"; }
    public String getVar() { return var; }
    public String getMileage() { return mileage; }
}

class Car {
    protected Engine engine;
    protected Engine getEngine() { return engine; }
}

class MyCar extends Car {
    protected PetrolEngine engine;
}
1 голос
/ 07 февраля 2012

Вам действительно нужен новый класс для представления вашего автомобиля (я полагаю, это то, что вы подразумеваете под MyCar).Конечно, ваша машина - это просто экземпляр автомобиля, но с деталями, специфичными для вас?Я хотел бы предложить, чтобы ваш класс автомобиля выглядел следующим образом:

public class Car {
    private Engine engine;

    public Car(Engine engine) {
        this.engine = engine;
    }

    public Engine getEngine() {
        return engine;
    }

}

Теперь, если вы хотите создать экземпляр своего автомобиля, вы можете сделать:

Car myCar = new Car(new PetrolEngine());

Далее ваш класс Engine должен быть либо интерфейсомили абстрактно, в зависимости от того, есть ли у вас какое-то базовое поведение, общее для всех двигателей, например:

public abstract class Engine {
    private final String type;

    protected Engine(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }

    public abstract String getMileage();
}

, затем для PetrolEngine вы делаете:

public class PetrolEngine extends Engine {
    public PetrolEngine() {
        super("Petrol Engine");
    }

    public String getMileage() {
        // Implement this however you calculate mileage for an Engine
        return "100";
    }
}

Обратите внимание на использованиезащищенный конструктор в Engine, это заставляет все подклассы явно вызывать этот конструктор и, следовательно, указывать тип двигателя, который заменяет ваши методы getVar.

Если вы действительно хотите иметь отдельный класс для вашего автомобиля с PetrolEngineТогда вы можете сделать это:

public class MyCar extends Car {
    public MyCar() {
        super(new PetrolEngine());
    }
}
1 голос
/ 07 февраля 2012

Во-первых, почему вы переопределяете метод getVar, когда он выполняет ту же работу?Ты вообще понимаешь, как продлевает работу?Вы должны прочитать о наследовании класса в Java.

Вот рабочее решение:

public class Test { 

    public static void main(String[] args) {
        MyCar myCar = new MyCar();
        String mileage = myCar.getEngine().getMileage();

    }
}

class Engine {
    protected String var = "Engine";
    protected String mileage = "0";

    protected String getVar() {
        return this.var;
    }

    protected String getMileage() {
        return mileage;
    }
}

class PetrolEngine extends Engine {

    PetrolEngine() {
        super();
        var = "PetrolEngine";
        mileage = "100";
    }
}

class Car {
    protected Engine engine;

    protected Engine getEngine() {
        return engine;
    }
}

class MyCar extends Car {

    MyCar() {
        engine = new PetrolEngine();
    }
}
1 голос
/ 07 февраля 2012

Нет, вам нужно либо привести к PetrolEngine, или, что еще лучше, потянуть метод getMileage() до Engine.

Это может быть бесполезный пример с игрушкой, но если это реально, я 'Укажу, что я хотел бы измерить эффективность использования топлива, будь то двигатель, сожженный бензином, растительным маслом или мочой обезьяны.Этот метод имеет смысл для более высокого класса.

Если вы беспокоитесь о «вычислении» его по-разному для различных типов Engine, сделайте его абстрактным или сделайте Engine интерфейсом.

0 голосов
/ 08 февраля 2012

Может быть, вам нужен новый интерфейс IEngineWithM Пробег. У него есть один метод, называемый GetMilet (). Позвольте PetrolEngine реализовать IEngineWithM Пробег. В вашем основном методе проверьте, реализует ли Engine IEngineWithM‹ - если это так, вызовите GetMilet (), в противном случае продолжайте свой веселый путь. Вроде как (извините, это C #):

internal interface IEngineWithMileage
{
    int GetMileage();
}

internal abstract class Engine
{
}

internal class RegularOldEngine : Engine
{

}

internal class PetrolEngine : Engine, IEngineWithMileage
{
    public int GetMileage()
    {
        return 100; //your code goes here.
    }
}

internal class Car
{
    public Engine Engine { get; set; }
}

internal class PetrolCar : Car
{

}

class Program
{
    static void Main(string[] args)
    {
        var petrolCar = new PetrolCar();
        var engine = petrolCar.Engine;

        if (engine is IEngineWithMileage)
        {
            var mileage = (engine as IEngineWithMileage).GetMileage();
        }
        else
        {
           //do whatever you need to do if there is no mileage
        }

    }
}
0 голосов
/ 07 февраля 2012

Вы можете рассмотреть возможность использования интерфейса вместо класса для Engine class.

0 голосов
/ 07 февраля 2012

Вот некоторые изменения, которые я бы сделал:

  • Переместите метод getM Пробег () до класса Engine и верните туда значение по умолчанию
  • Просто перезаписать getM Пробег () , когда вам нужно вернуть другое значение
  • Для пробега используйте int, float или double, а не число в строке
  • Вам не нужно перезаписывать метод getVar () в подтипах снова, когда вы возвращаете только this.var. Это уже сделано в классе Engine
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...