make overriding方法返回不同的类型

时间:2012-02-07 10:45:58

标签: java oop design-patterns

假设我有以下类:

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的{​​{1}}关联成PetrolCar?在上面的例子中,它创建了两个单独的实例变量PetrolEngine,一个engine类型在PetrolEngine内,另一个PetrolCar,我可以访问Engine来自super.getEngine()内部。我不想要不同的变量。我希望代码应该知道“PetrolCar”的“Engine”是PetrolCar

编辑:将MyCar更改为PetrolCar以避免混淆,这导致每个人都认为MyCar应该是Car的实例而不是子类型。

9 个答案:

答案 0 :(得分:2)

我建议您将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 :(得分:2)

是的,你可以。他们称之为协变返回(从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 :(得分:2)

给兔子上皮的方法不止一种。

最重要的是,get(和set)方法往往表示不感兴趣的对象。把行为放在对象中要好得多,而不是让程序脚本在外面运行。

该方法可能适用于所有引擎,因此可以移动到基类。

您可以将Car子类化为不同类型的引擎

Car可以设为通用class Car<E extends Engine> {

在特殊情况下,小游客可能是合适的。将方法添加到Enginepublic void accept(EngineVisitor visitor) interface EngineVisitor { void visit(Engine engine); void visit(PetrolEngine engine); }

(结合其他评论:一个类通常是一个好的想法,要么是抽象的,要么是一个叶子扩展的具体类通常是坏的。通常应该谨慎使用实现继承,而protected确实很少。你打算不改变字段,标记它们final。)

答案 3 :(得分:1)

不,您必须转发到PetrolEngine,或者更好的是,将getMileage()方法提升到Engine

这可能是无用的玩具示例,但如果它是真实的我会指出我想要燃油效率测量引擎是否燃烧汽油,植物油或猴子尿液。该方法对较高级别有意义。

如果您担心不同Engine类型的“计算”方式不同,请将其设为摘要或将Engine设为接口。

答案 4 :(得分:1)

首先,为什么在执行相同的工作时重新实现getVar方法?你甚至了解延伸工作的方式吗?您应该阅读java中的类Inheritence。

这是一个有效的解决方案:

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

答案 5 :(得分:1)

你真的需要一个新的类来代表你的车(我猜你的意思是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());
    }
}

答案 6 :(得分:0)

以下是我要做的一些改变:

  • getMileage()方法移至Engine类并返回默认值
  • 当您需要返回其他值时,只需覆盖 getMileage()
  • 使用int,float或double作为里程,而不是字符串中的数字
  • 当您只返回this.var时,您不必再次覆盖子类型中的 getVar()方法。这已在Engine类
  • 中完成

答案 7 :(得分:0)

您可以考虑使用界面而不是引擎类的类。

答案 8 :(得分:0)

也许您需要一个名为IEngineWithMileage的新界面。它有一个名为GetMileage()的方法。让PetrolEngine实现IEngineWithMileage。在您的main方法中,检查引擎是否实现IEngineWithMileage - 如果是,请调用GetMileage(),否则,继续您的快乐方式。像这样(抱歉,这是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
        }

    }
}