Reputation: 35405
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
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
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
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
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
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
Reputation: 40985
You might consider using an interface instead of a class for the Engine class.
Upvotes: 0
Reputation: 12518
Here are some changes I would do:
Upvotes: 0
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
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