我的车库里真的有车吗?

2022-08-31 05:31:23

我是Java编程的新手,试图掌握OOP的窍门。

所以我构建了这个抽象类:

public abstract class Vehicle{....}

和 2 个子类:

public class Car extends Vehicle{....}
public class Boat extends Vehicle{....}

Car并且还保留了一些不常见的唯一字段和方法(没有相同的名称,因此我无法在Carue中为它们定义抽象方法)。Boat

现在在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();

所以我的问题是 - 我的车库里到底有什么?我试图获得直觉,并理解“幕后”发生的事情。


为了将来的读者,以下是答案的简短摘要:

  1. 是的,有一个CarmyGarage[]
  2. 作为一种静态类型化语言,Java编译器不会允许访问非“Vehicle”的方法/字段,如果通过基于 Vehicle 超类的数据结构(例如Vehicle myGarage[])
  3. 至于如何解决,下面有2种主要方法:
    1. 使用类型转换,这将减轻编译器的担忧,并将设计中的任何错误留给运行时
    2. 我需要铸造的事实表明设计是有缺陷的。如果我需要访问非车辆功能,那么我不应该将汽车和船只存储在基于车辆的数据结构中。要么使所有这些功能都属于 Vehicle,要么使用更具体(派生)的基于类型的结构
  4. 在许多情况下,组合和/或接口将是继承的更好替代方案。可能是我下一个问题的主题...
  5. 再加上许多其他好的见解,如果一个人确实有时间浏览答案的话。

答案 1

如果您需要在车库之间做出区分,那么您应该将它们存储在不同的结构中。CarBoat

例如:

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

每个都有一个价格,所以可以把它放在抽象类中。VehicleVehicle

然而,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));  
    }
}

该类可能具有其他定义和特定的属性和方法。BoatgetPriceAfterYears

所以现在回到课堂上,你可以定义: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();
}

多态性的兴趣在于能够在不关心实现的情况下调用 a。getPriceAfterYearsVehicle

通常,下倾是设计有缺陷的标志:如果您需要区分车辆的实际类型,请不要将车辆全部存放在一起。

注意:当然,这里的设计可以很容易地改进。这只是一个示例来演示这些要点。


答案 2

要回答您的问题,您可以找出车库中的确切内容,请执行以下操作:

Vehicle v = myGarage[0];

if (v instanceof Car) {
   // This vehicle is a car
   ((Car)v).doSomeCarStuff();
} else if(v instanceof Boat){
   // This vehicle is a boat
   ((Boat)v).doSomeBoatStuff();
}

更新:您可以从下面的评论中阅读,此方法对于简单的解决方案是可以的,但这不是很好的做法,特别是如果您的车库中有大量车辆。因此,只有当您知道车库将保持较小时,才使用它。如果不是这种情况,请在堆栈溢出上搜索“避免实例”,有多种方法可以做到这一点。


推荐