我是Java编程的新手,试图掌握OOP的诀窍。
所以我创建了这个抽象类:
public abstract class Vehicle{....}
和2个子类:
public class Car extends Vehicle{....}
public class Boat extends Vehicle{....}
Car和Boat还拥有一些不常见的独特字段和方法(没有相同的名称,所以我无法在Vehicle中为它们定义抽象方法)。
现在在mainClass中我已经设置了我的新车库:
Vehicle[] myGarage= new Vehicle[10];
myGarage[0]=new Car(2,true);
myGarage[1]=new Boat(4,600);
我对多态性非常满意,直到我尝试访问Car特有的一个字段,例如:
boolean carIsAutomatic = myGarage[0].auto;
编译器不接受。我使用类型转换解决了这个问题:
boolean carIsAutomatic = ((Car)myGarage[0]).auto;
工作……但它对方法没有帮助,只对字段有帮助。意思是我做不到
(Car)myGarage[0].doSomeCarStuff();
我的问题是,我的车库里到底有什么?我试图获得直觉,并理解“幕后”发生了什么。
为了方便以后的读者,以下是对答案的简要总结:
Yes, there's a Car in myGarage[]
Being a static typed language, the Java compiler will not lend access to methods/fields that are non-"Vehicle", if accessing those through a data structure based on the Vehicle super class( such as Vehicle myGarage[])
As for how to solve, there are 2 main approaches below:
Use type casting, which will ease the compiler's concerns and leave any errors in the design to run time
The fact that I need casting says the design is flawed. If I need access to non-Vehicle capabilities then I shouldn't be storing the Cars and Boats in a Vehicle based data structure. Either make all those capabilities belong to Vehicle, or use more specific (derived) type based structures
In many cases, composition and/or interfaces would be a better alternative to inheritance. Probably the subject of my next question...
Plus many other good insights down there, if one does have the time to browse through the answers.
我真的只是汇集了其他人的想法(我不是Java的人,所以这是伪的而不是实际的),但是,在这个人为的例子中,我将把我的汽车检查方法抽象为一个专门的类,它只知道汽车,只关心查看车库时的汽车:
abstract class Vehicle {
public abstract string getDescription() ;
}
class Transmission {
public Transmission(bool isAutomatic) {
this.isAutomatic = isAutomatic;
}
private bool isAutomatic;
public bool getIsAutomatic() { return isAutomatic; }
}
class Car extends Vehicle {
@Override
public string getDescription() {
return "a car";
}
private Transmission transmission;
public Transmission getTransmission() {
return transmission;
}
}
class Boat extends Vehicle {
@Override
public string getDescription() {
return "a boat";
}
}
public enum InspectionBoolean {
FALSE, TRUE, UNSUPPORTED
}
public class CarInspector {
public bool isCar(Vehicle v) {
return (v instanceof Car);
}
public bool isAutomatic(Car car) {
Transmission t = car.getTransmission();
return t.getIsAutomatic();
}
public bool isAutomatic(Vehicle vehicle) {
if (!isCar(vehicle)) throw new UnsupportedVehicleException();
return isAutomatic((Car)vehicle);
}
public InspectionBoolean isAutomatic(Vehicle[] garage, int bay) {
if (!isCar(garage[bay])) return InspectionBoolean.UNSUPPORTED;
return isAutomatic(garage[bay])
? InspectionBoolean.TRUE
: InspectionBoolean.FALSE;
}
}
重点是,当你问汽车的传动装置时你已经决定只关心汽车了。所以只要问问CarInspector就行了。多亏了三状态枚举,你现在可以知道它是自动的,甚至它不是一辆车。
当然,你需要为你关心的每辆车使用不同的vehicleinspector。你已经把哪个VehicleInspector实例化的问题推到了链上。
因此,您可能想要查看接口。
抽象getTransmission到接口(例如hastrtransmission)。这样,你就可以检查车辆是否有变速器,或者写一个TransmissionInspector:
abstract class Vehicle { }
class Transmission {
public Transmission(bool isAutomatic) {
this.isAutomatic = isAutomatic;
}
private bool isAutomatic;
public bool getIsAutomatic() { return isAutomatic; }
}
interface HasTransmission {
Transmission getTransmission();
}
class Car extends Vehicle, HasTransmission {
private Transmission transmission;
@Override
public Transmission getTransmission() {
return transmission;
}
}
class Bus extends Vehicle, HasTransmission {
private Transmission transmission;
@Override
public Transmission getTransmission() {
return transmission;
}
}
class Boat extends Vehicle { }
enum InspectionBoolean {
FALSE, TRUE, UNSUPPORTED
}
class TransmissionInspector {
public bool hasTransmission(Vehicle v) {
return (v instanceof HasTransmission);
}
public bool isAutomatic(HasTransmission h) {
Transmission t = h.getTransmission();
return t.getIsAutomatic();
}
public bool isAutomatic(Vehicle v) {
if (!hasTranmission(v)) throw new UnsupportedVehicleException();
return isAutomatic((HasTransmission)v);
}
public InspectionBoolean isAutomatic(Vehicle[] garage, int bay) {
if (!hasTranmission(garage[bay])) return InspectionBoolean.UNSUPPORTED;
return isAutomatic(garage[bay])
? InspectionBoolean.TRUE
: InspectionBoolean.FALSE;
}
}
现在你说,你只关心变速器,而不考虑车辆,所以可以问变速器检查员。TransmissionInspector可以检查公共汽车和汽车,但它只能询问有关变速器的信息。
现在,您可能认为布尔值并不是您所关心的全部。在这一点上,你可能更喜欢使用泛型的Supported类型,它同时暴露了受支持的状态和值:
class Supported<T> {
private bool supported = false;
private T value;
public Supported() { }
public Supported(T value) {
this.isSupported = true;
this.value = value;
}
public bool isSupported() { return supported; }
public T getValue() {
if (!supported) throw new NotSupportedException();
return value;
}
}
现在你的检查器可能被定义为:
class TransmissionInspector {
public Supported<bool> isAutomatic(Vehicle[] garage, int bay) {
if (!hasTranmission(garage[bay])) return new Supported<bool>();
return new Supported<bool>(isAutomatic(garage[bay]));
}
public Supported<int> getGearCount(Vehicle[] garage, int bay) {
if (!hasTranmission(garage[bay])) return new Supported<int>();
return new Supported<int>(getGearCount(garage[bay]));
}
}
正如我说过的,我不是一个Java人,所以上面的一些语法可能是错误的,但这些概念应该是成立的。然而,在没有测试之前,不要在任何重要的地方运行上面的代码。
这是应用访问者设计模式的好地方。
这种模式的美妙之处在于,您可以在一个超类的不同子类上调用不相关的代码,而不必到处执行奇怪的强制转换,也不必在超类中放入大量不相关的方法。
这是通过创建一个Visitor对象并允许我们的Vehicle类接受()访问者来实现的。
您还可以创建许多类型的Visitor,并使用相同的方法调用不相关的代码,只是不同的Visitor实现,这使得这种设计模式在创建干净的类时非常强大。
举个例子:
public class VisitorDemo {
// We'll use this to mark a class visitable.
public static interface Visitable {
void accept(Visitor visitor);
}
// This is the visitor
public static interface Visitor {
void visit(Boat boat);
void visit(Car car);
}
// Abstract
public static abstract class Vehicle implements Visitable {
// NO OTHER RANDOM ABSTRACT METHODS!
}
// Concrete
public static class Car extends Vehicle {
public void doCarStuff() {
System.out.println("Doing car stuff");
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// Concrete
public static class Boat extends Vehicle {
public void doBoatStuff() {
System.out.println("Doing boat stuff");
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// Concrete visitor
public static class StuffVisitor implements Visitor {
@Override
public void visit(Boat boat) {
boat.doBoatStuff();
}
@Override
public void visit(Car car) {
car.doCarStuff();
}
}
public static void main(String[] args) {
// Create our garage
Vehicle[] garage = {
new Boat(),
new Car(),
new Car(),
new Boat(),
new Car()
};
// Create our visitor
Visitor visitor = new StuffVisitor();
// Visit each item in our garage in turn
for (Vehicle v : garage) {
v.accept(visitor);
}
}
}
如您所见,StuffVisitor允许您在Boat或Car上调用不同的代码,这取决于调用哪个访问实现。您还可以创建Visitor的其他实现,以使用相同的.visit()模式调用不同的代码。
还要注意,使用此方法时,不使用instanceof或任何hack类检查。类之间唯一重复的代码是方法void accept(Visitor)。
例如,如果你想支持3种类型的具体子类,你也可以将该实现添加到Visitor接口中。