我是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.
You defined that your garage will store vehicles, so you do not care what type of vehicles you have. The vehicles have common features like engine, wheel, behavior like moving.
The actual representation of these features might be different, but at abstract layer are the same.
You used abstract class which means that some attributes, behaviors are exactly the same by both vehicle. If you want to express that your vehicles have common abstract features then use interface like moving might mean different by car and boat. Both can get from point A to point B, but in a different way (on wheel or on water - so the implementation will be different)
So you have vehicles in the garage which behave the same way and you do not car about the specific features of them.
回答评论:
接口是指描述如何与外部世界通信的契约。在合同中,你定义了你的车辆可以移动,可以操纵,但你没有描述它实际是如何工作的,它是在实现中描述的。通过抽象类,你可能有共享某些实现的函数,但你也有不知道如何实现的函数。
一个使用抽象类的例子:
abstract class Vehicle {
protected abstract void identifyWhereIAm();
protected abstract void startEngine();
protected abstract void driveUntilIArriveHome();
protected abstract void stopEngine();
public void navigateToHome() {
identifyWhereIAm();
startEngine();
driveUntilIArriveHome();
stopEngine();
}
}
每辆车都将使用相同的步骤,但步骤的实现将因车辆类型而异。汽车可能会使用GPS,船只可能会使用声纳来确定它的位置。
如果你需要在车库中区分Car和Boat,那么你应该将它们存储在不同的结构中。
例如:
public class Garage {
private List<Car> cars;
private List<Boat> boats;
}
然后,您可以定义特定于船或特定于汽车的方法。
那为什么要多态呢?
假设Vehicle是这样的:
public abstract class Vehicle {
protected int price;
public getPrice() { return price; }
public abstract int getPriceAfterYears(int years);
}
每个Vehicle都有一个价格,所以它可以放在Vehicle抽象类中。
然而,决定n年后价格的公式取决于车辆,因此留给实现类来定义它。例如:
public Car extends Vehicle {
// car specific
private boolean automatic;
@Override
public getPriceAfterYears(int years) {
// losing 1000$ every year
return Math.max(0, this.price - (years * 1000));
}
}
Boat类可能对getPriceAfterYears和特定的属性和方法有其他定义。
现在回到Garage类,你可以定义:
// car specific
public int numberOfAutomaticCars() {
int s = 0;
for(Car car : cars) {
if(car.isAutomatic()) {
s++;
}
}
return s;
}
public List<Vehicle> getVehicles() {
List<Vehicle> v = new ArrayList<>(); // init with sum
v.addAll(cars);
v.addAll(boats);
return v;
}
// all vehicles method
public getAveragePriceAfterYears(int years) {
List<Vehicle> vehicules = getVehicles();
int s = 0;
for(Vehicle v : vehicules) {
// call the implementation of the actual type!
s += v.getPriceAfterYears(years);
}
return s / vehicules.size();
}
多态性的兴趣在于能够调用车辆上的getPriceAfterYears而不关心实现。
通常,下倾是设计缺陷的标志:如果需要区分车辆的实际类型,请不要将所有车辆存储在一起。
注:当然这里的设计可以很容易地改进。这只是一个例子来证明这一点。