似乎抽象类意味着类的定义不完整,因此无法实例化。
我看到了一些简单的Java代码,它有一个抽象类,定义了所有方法。然后我想知道,为什么他们把它作为抽象类而不是真正的类呢?他们这样做是为了让我们无法从这个抽象类中实例化吗?或者他们从定义所有内容的抽象类中获得其他好处?
答案 0 :(得分:43)
即使所有方法都有默认实现,但这些实现可能在应用程序的上下文中实际上并非有意义。这些方法可能只进行内部簿记,而实际有用的实现必须由派生类提供,派生类执行它需要做的事情,然后调用超类方法。
然而,这只是猜测。你必须展示一个实际的例子来说明这个设计可能是什么原因。
举个例子,让我们来看一个简单的游戏引擎。我的游戏中有很多不同的GameObject
s。
有些是可见的,所以我的基础类获得draw()
方法。但是可能会有一些看不见的对象,比如根本没有出现的触发区域,所以我将它作为基类中的无操作实现。
有些人在碰到某些东西时会做某事,所以每个人都会得到一个collide(other)
方法。但有些人在碰撞时就不会做任何事情,就像纯粹的视觉粒子效果一样,所以我也在基类中提供了无操作。
有些人会在每场比赛中做点什么,所以他们得到update()
方法。但有些物体,如墙壁,可能根本不会做任何事情。所以我也为此提供了无操作。
那么,当我有一个看不见的物体,不做任何事情而且不与任何东西互动时,我该怎么办?在游戏中没有理由这样做。所以我做了这个基础课abstract
。 理论上你可以实例化它,因为所有方法都有一个实现,但几乎你没有理由这样做,当你尝试时,你误解了我的游戏引擎是如何工作的。
答案 1 :(得分:22)
一个典型的用例是创建适配器类。考虑一个回调接口,您可以在其中收到10个不同事件的通知,但通常只对其中一些事件感兴趣。使用适配器类,您可以提供空实现,这样实际的回调只需要在扩展适配器后实现那些感兴趣的方法。通过使适配器抽象化,您表达了这样一个事实:实例化适配器本身是没有意义的,因为它没有任何用处。
从Java 8开始,您将不再实现此类适配器,而是使用默认方法作为接口。
答案 2 :(得分:11)
是的,有。有时候你知道你正在创建一个抽象类 - 一个必须从中派生出来才能产生任何实际意义的类,但你想为所有方法提供一个默认实现。这并没有发生太多,但它确实发生了。
更新: 我刚才有这样的案子。我正在录制各种用户生成的事件。每个事件都有一个TimeSpan和一个描述,它们都有其他的东西。我创建了一个基本事件类:
public abstract class BaseEvent
{
public TimeSpan EventTime {get; private set;}
public string Description {get; protected set;}
public BaseEvent(TimeSpan time, string desc) ...
}
当然,这是C#而不是Java,但如果我用Java编写,我本可以做同样的事情)
答案 3 :(得分:7)
您可以拥有一个实现多个接口的抽象类。您不需要在抽象类中实现这些方法,但是您需要在抽象类的子类中实现它们。
E.g。
public interface MyInterface{
void hello();
}
public abstract class Clazzy implements MyInterface {
//I need not have the interface method.
}
public class MySubclass extends Clazzy {
@Override
public void hello() {
// I need to be here
}
}
答案 4 :(得分:4)
如果一个Java类被声明为abstract而且声明了它的所有方法,那么它们就是这样做的,所以它不能被实例化,尽管它可能是子类。
"抽象类不能被实例化,但它们可以被子类化。" 见这里:https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html
答案 5 :(得分:4)
您可能有一些课程在您的应用程序的上下文中没有意义,但他们在设计中做了。 一个愚蠢的例子: 抽象类动物,实现生而死。你不想要一个"动物"。你想要一只狗。 这样您就不必重复添加每个类的代码。现在你可以拥有狗,猫或任何你想要的东西,这是一个很好的理由,无论如何它很难找到。
答案 6 :(得分:3)
我看到这个问题已经标记为已回答,但我想提供另一种替代解释。
这种代码的另一个原因是为该抽象类的所有派生类提供基本实现。
然后,当实现新的派生类时,您有一个可运行的实现,然后您可以选择覆盖这些方法,以便为该派生实例自定义行为。
以BaseItemBuilder
为例,它是抽象的,并提供基于公共参数的数据访问逻辑,例如ItemNumber。
(对不起C#代码,但这个问题几乎只是编程理论而不是特定语言)
public abstract class BaseItemBuilder { /*I don't want anyone using this directly but I do want the base specification*/
public virtual void GetInfoAboutItem(int itemNumber){
var infoModel = _infoDataService.GetItemInfo(itemNumber);
//project values on to internal representation of item
}
public virtual void GetMoreInfoAboutItem(int itemNumber){
var infoModel = _infoDataService.GetMoreItemInfo(itemNumber);
//project values on to internal representation of item
}
public virtual void GetEvenMoreInfoAboutItem(int itemNumber){
var infoModel = _infoDataService.GetEvenMoreItemInfo(itemNumber);
//project values on to internal representation of item
}
public virtual void GetYetMoreInfoAboutItem(int itemNumber){
var infoModel = _infoDataService.GetYetMoreItemInfo(itemNumber);
//project values on to internal representation of item
}
}
然后,您可以派生使用基本实现的NormalItemBuilder
。这占您跟踪的项目的80%。
public class BaseItemBuilder {
/*I use the base implementation!*/
}
然后,您派生SpecialType1ItemBuilder
转到几个不同的数据服务,以获取有关该特定项类型的信息,但仍使用一些基本实现默认值。这涵盖了接下来的10%。
public class SpecialType1ItemBuilder { /*I don't want anyone using this directly but I do want the base specification*/
public override void GetInfoAboutItem(int itemNumber){
var infoModel = _infoDataService.GetType1ItemInfo(itemNumber);
//project values on to internal representation of item
}
public override void GetYetMoreInfoAboutItem(int itemNumber){
var infoModel = _infoDataService.GetYetMoreType1ItemInfo(itemNumber);
//project values on to internal representation of item
}
}
然后你派生出一个'SpecialType2ItemBuilder',转到另一组零星的数据服务来完成该项目的图片。这个覆盖了另一组与Type2ItemBuilder不同的方法。这涵盖了你的最后10%。
public class SpecialType2ItemBuilder { /*I don't want anyone using this directly but I do want the base specification*/
public override void GetInfoAboutItem(int itemNumber){
var infoModel = _infoDataService.GetType2ItemInfo(itemNumber);
//project values on to internal representation of item
}
public override void GetMoreInfoAboutItem(int itemNumber){
var infoModel = _infoDataService.GetMoreType2ItemInfo(itemNumber);
//project values on to internal representation of item
}
}