Hari Menon
Hari Menon

Reputation: 35405

Make overriding method return a different type

Suppose I have the following classes:

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

Obviously myCar.getEngine().getMileage() will not work because getEngine() will return an Engine instance, not a PetrolEngine instance. What is the workaround for this? I don't want to redefine getEngine() in all subtypes of Car, at the same time I would like to get back the more specific type PetrolEngine when I call getEngine() on an instance of PetrolCar, without having to type cast. Is this possible?

In other words, is there a way I can associate the Engine of PetrolCar to be a PetrolEngine? In the above case, it is creating two separate instance variables engine, one of PetrolEngine type inside PetrolCar, and another Engine, which I can access as super.getEngine() from inside PetrolCar. I don't want different variables. What I would like is that the code should know that the "Engine" of "PetrolCar" is a PetrolEngine.

EDIT: Changed MyCar to PetrolCar to avoid confusion which is leading everyone to believe that MyCar should be an instance of Car rather than a subtype.

Upvotes: 3

Views: 341

Answers (9)

Henry Fieger
Henry Fieger

Reputation: 346

Maybe you need a new interface called IEngineWithMileage. It has one method called GetMileage(). Let PetrolEngine implement IEngineWithMileage. In your main method, check to see if the Engine implements IEngineWithMileage - if so, call GetMileage(), otherwise, continue on your merry way. Sort of like this (sorry, it's 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
        }

    }
}

Upvotes: 0

user381105
user381105

Reputation:

Yes you can. They call it covariant return (starting from 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();
}

Upvotes: 2

DaveJohnston
DaveJohnston

Reputation: 10151

Do you really need a new class to represent your car (I assume that is what you mean by MyCar). Surely your car is just an instance of Car but with the details specific to you? I would suggest your car class should look like this:

public class Car {
    private Engine engine;

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

    public Engine getEngine() {
        return engine;
    }

}

Now if you want to instantiate your car you can do:

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

Next your Engine class should either be an interface or abstract, depending on whether or not you have some basic behaviour common amongst all engines, e.g.:

public abstract class Engine {
    private final String type;

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

    public String getType() {
        return type;
    }

    public abstract String getMileage();
}

then for a PetrolEngine you do:

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

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

Note the use of the protected constructor in Engine, this forces all subclasses to explicitly call that constructor and therefore specify the engine type, which replaces your getVar methods.

If you really do want to have a separate class for your car that has a PetrolEngine, then you could do this:

public class MyCar extends Car {
    public MyCar() {
        super(new PetrolEngine());
    }
}

Upvotes: 1

Tom Hawtin - tackline
Tom Hawtin - tackline

Reputation: 147144

There is more than one way to skin a rabbit.

Most importantly, get (and set) methods tend to indicate uninteresting objects. Much better to put the behaviour in the object, rather than have a procedural script run outside.

The method may be appropriate to all engines, so could be moved to the base class.

You could subclass Car for different types of engine

Car could be made generic class Car<E extends Engine> {.

In unusual cases, a small visitor may be appropriate. Add a method to Engine, public void accept(EngineVisitor visitor) where interface EngineVisitor { void visit(Engine engine); void visit(PetrolEngine engine); }.

(Couple other comments: It's generally a good idea for a class to be either abstract or a leaf - extending concrete classes is usually bad. Implementation inheritance should generally be used sparingly, and protected very rarely indeed. If you intend fields not to change, mark them final.)

Upvotes: 2

Adel Boutros
Adel Boutros

Reputation: 10285

First, why do you reimplement the getVar method when it does the same work ? Do you even understand the way Extends work? You should read about class Inheritence in java.

Here is a working solution:

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

Upvotes: 1

Frankline
Frankline

Reputation: 40985

You might consider using an interface instead of a class for the Engine class.

Upvotes: 0

Prine
Prine

Reputation: 12518

Here are some changes I would do:

  • Move the getMileage() Method up to the Engine class and return there a default value
  • Just overwrite getMileage() when you need to return a different value
  • Use int, float or double for mileage and not a number in a string
  • You don't have to overwrite the getVar() Method in the subtypes again, when you return just this.var. This is already done in the Engine class

Upvotes: 0

Matt
Matt

Reputation: 17629

I suggest you turn Engine into an interface:

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

Upvotes: 2

duffymo
duffymo

Reputation: 308743

No, you either have to cast to PetrolEngine or, better yet, pull the getMileage() method up to Engine.

This might be useless toy example, but if it's real I'll point out that I would want fuel efficiency measurements whether the engine burned petrol, vegetable oil, or monkey urine. The method makes sense on the higher class.

If you're worried about "calculating" it differently for different Engine types, make it abstract or make Engine an interface.

Upvotes: 1

Related Questions