这对许多人来说可能是一个简单的问题,但让我很困惑。我从Kathy Sierra中选择了一个示例,它展示了抽象类的实用性,但我无法理解抽象类的整体重要性。
示例
我们有一个抽象类Car
和抽象方法 - power()
& topSpeed()
。这些方法在子类 BMW , Volkswagen 和 Audi 中实现。
我的问题是 - 为什么我们首先需要抽象类Car
来定制每种车型的方法?为什么不在这些汽车的任何一种汽车中使用这两种方法,宝马和其他两种 - 大众汽车和奥迪 - 可以简单地改写这些方法吗?
答案 0 :(得分:6)
通过使方法抽象,意味着人们必须实现它。你需要人们这样做,人们不可能忘记这样做,因为如果他们这样做就无法编译。
@override
注释存在的原因非常相似,通过将方法标记为@override
,如果(例如)键入方法名称错误且实际上没有覆盖某些内容,则会出现错误。
在很多方面,抽象类是接口和普通类之间的一半 - 它定义了你需要做什么才能以与接口相同的方式使用它,但它也为你处理了一些实现。 / p>
类只能扩展另一个类。他们可以实现任意数量的接口。
例如,MotorVehicle
,Car
和Motorbike
可能会继承Train
,但您可能会Steerable
实现Car
接口},Motorbike
和Pedalbike
。
回答评论中的问题:
如果有一个接口“I”的方法m()由类“A”实现而另一个类“B”想要访问方法m(),那么接口的需求是什么。我们可以简单地在A类中实现该方法吗?
您可以 - 但另一方面,如果课程B
想要在课程m()
和课程A
(其中A和C课程)中访问方法C
不继承彼此或包含m()的公共类,那么这样做的方法是指定一个公共接口I,而类B使用接口类型,I,而不是类型A和C。
还要记住,可以在包和库之间使用接口。例如, Listener 和策略模式大量使用接口。当Java开发人员编写JButton
(例如)时,ActionLstener
被指定为接口,以便为将来使用JButtons
的人提供最大的灵活性。
答案 1 :(得分:1)
为什么不在这些汽车的任何一种汽车中使用这两种方法,宝马和其他两种 - 大众汽车和奥迪 - 可以简单地改写这些方法吗?
这不需要Volkswagen
和Audi
从BMW
继承吗?这不正确,因为那些不是宝马。
抽象基类及其抽象方法构成了一种继承对象必须实现的契约。没有具体的Car
这样的东西不是更具体的类型。但所有具体类型都有共性。摘要Car
强制执行这些共性,允许在需要时将特定类型视为Car
的通用实例。
例如,如果您的代码需要Car
,它将调用Drive()
方法,那么该代码不关心{{1}的种类它得到了。它将Car
任意Drive()
。所以它接受一个抽象类型,以便可以使用任何实现该类型的东西:
Car
如果不执行合同,静态类型和编译语言将无法保证对象上存在UseACar(Car car)
{
car.Drive();
}
// elsewhere
BMW myCar = new BMW();
UseACar(myCar);
。您需要为每种可能类型的汽车创建.Drive()
方法,每种方法都接受不同的类型。使用这样的多态性允许您创建一个更通用的方法,该方法适用于抽象类型的任何实例。
答案 2 :(得分:0)
这样您就可以编写处理Car
的代码而不知道它是什么类型的汽车:
public void PrintTopSpeed(Car car)
{
System.out.println("This car's top speed is " + car.topSpeed());
}
如果Car
类没有定义topSpeed()
,则此代码将无法编译。您必须为每个BMW,Volkswagen,Audi等派生类提供不同版本的打印功能。这可能是面向对象编程中最基本的概念,所以你真的需要掌握它。基类允许对象共享共同的行为,并允许编写代码以使用该行为,而无需了解它正在处理的特定类型的对象。
答案 3 :(得分:0)
多态性就是答案。如果你在抽象类中没有方法power()和topSpeed(),你就不能这样做:
List<Car> cars;
cars.add(new Porshe());
cars.add(new Ford());
for(Car car : cars){
System.out.println(car.topSpeed());
}
如果您的子类中只有自定义方法,那么您必须自己处理很多事情。
答案 4 :(得分:0)
这称为抽象。抽象类中的方法被认为是协议,因此汽车制造商不应该违反。
答案 5 :(得分:0)