假设我有以下类:
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的实例而不是子类型。
答案 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> {
。
在特殊情况下,小游客可能是合适的。将方法添加到Engine
,public 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)
以下是我要做的一些改变:
答案 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
}
}
}