我知道应用于抽象类的语法,规则,并且想知道抽象类的用法
抽象类不能直接实例化,但可以被其他类扩展
这样做的好处是什么?
它与接口有何不同?
我知道一个类可以实现多个接口,但只能扩展一个抽象类。接口和抽象类之间的唯一区别是吗?
我知道接口的用法。我从Java中AWT的事件委托模型中学到了这一点。
在哪种情况下我应该将类声明为抽象类?那有什么好处?
我知道应用于抽象类的语法,规则,并且想知道抽象类的用法
抽象类不能直接实例化,但可以被其他类扩展
这样做的好处是什么?
它与接口有何不同?
我知道一个类可以实现多个接口,但只能扩展一个抽象类。接口和抽象类之间的唯一区别是吗?
我知道接口的用法。我从Java中AWT的事件委托模型中学到了这一点。
在哪种情况下我应该将类声明为抽象类?那有什么好处?
Answers:
这个答案很好地解释了抽象类和接口之间的区别,但是没有回答为什么要声明一个。
从纯技术的角度来看,从来就没有要求声明一个类为抽象。
考虑以下三个类:
class Database {
public String[] getTableNames() { return null; } //or throw an exception? who knows...
}
class SqlDatabase extends Database { } //TODO: override getTableNames
class OracleDatabase extends Database { } //TODO: override getTableNames
你不具备做数据库抽象类,即使有,其实现一个明显的问题:当你写这个程序,你可以输入new Database()
,这将是有效的,但它永远不会成功。
无论如何,您仍然会得到多态性,因此,只要您的程序仅创建SqlDatabase
和OracleDatabase
实例,您就可以编写如下方法:
public void printTableNames(Database database) {
String[] names = database.getTableNames();
}
抽象类通过阻止开发人员实例化基类来改善这种情况,因为开发人员已将其标记为缺少功能。它还提供了编译时的安全性,因此您可以确保扩展抽象类的所有类都提供最低限度的功能,并且您不必担心放置继承者以某种方式拥有的存根方法(如上述方法)神奇地知道他们必须重写一个方法才能使它起作用。
接口是一个完全独立的主题。使用界面可以描述可以对对象执行的操作。在编写使用其他组件,对象的服务的方法,组件等时,通常会使用接口,但是您不在乎从中获取服务的对象的实际类型是什么。
请考虑以下方法:
public void saveToDatabase(IProductDatabase database) {
database.addProduct(this.getName(), this.getPrice());
}
您不必在乎database
对象是否从任何特定对象继承,而只在乎它是否具有addProduct
方法。因此,在这种情况下,接口比使所有类都继承自同一基类更合适。
有时两者的结合非常好。例如:
abstract class RemoteDatabase implements IProductDatabase {
public abstract String[] connect();
public abstract void writeRow(string col1, string col2);
public void addProduct(String name, Double price) {
connect();
writeRow(name, price.toString());
}
}
class SqlDatabase extends RemoteDatabase {
//TODO override connect and writeRow
}
class OracleDatabase extends RemoteDatabase {
//TODO override connect and writeRow
}
class FileDatabase implements IProductDatabase {
public void addProduct(String name, Double price) {
//TODO: just write to file
}
}
请注意,某些数据库如何从RemoteDatabase继承以共享某些功能(例如在写一行之前进行连接),但是FileDatabase是仅实现的单独类IProductDatabase
。
相似之处
抽象类和接口是抽象所必需的。不能使用new实例化它们,但是可以将其解析为反转控制容器或通过工厂模式。
区别
介面
抽象类
通过简单的Google查询实际上很容易找到答案。
它与接口有何不同?
在抽象类中,您可以实现一些方法,其余(强制)其余部分由扩展类来实现。您不能在接口中实现方法。扩展普通类时,您不能强迫任何人重写任何内容。使用抽象类,您可以。
接口和抽象类之间的主要区别是抽象类可以提供实现的方法。使用接口,您只能声明方法,并编写其签名。这是一个类的示例,该类扩展了实现两个接口的抽象类:(java)
interface MyInterface1 {
string getValue1();
}
interface MyInterface2 {
string getValue2();
}
abstract class MyAbstractClass implements MyInterface1, MyInterface2{
void printValues() {
System.out.println("Value 1: " + getValue1() + ", Value 2: " + getValue2() +
", Value 3: " + getValue3());
}
protected abstract string getValue3();
}
class ImpClass extends MyAbstractClass {
public string getValue1() {
return "1";
}
public string getValue2() {
return "2";
}
protected string getValue3() {
return "3";
}
}
在此示例中,MyAbstractClass提供了打印所有三个值的公共方法。在ImpClass中,您需要分别从MyInterface1和MyInterface2实现getValue1和getValue2,并从抽象类实现getValue3 。
Voilà。
还有更多方面(接口:仅公共方法,抽象类:受保护的抽象和公共抽象方法),但是您可以自己阅读。
最后,仅提供抽象方法的抽象类是“纯”抽象基类,也就是接口。
当您不希望允许开发人员(可能是您自己)实例化它时,可以声明一个类抽象,因为它行不通或没有意义。
例如,考虑存在不同类型游戏实体的游戏。它们都继承自基GameEntity
类。
abstract class GameEntity{
int lifePoint, speed, damage;
public attack(GameEntity target){ target.damage(damage); }
public damage(int damageInflicted){ lifePoint -= damageInflicted - speed; }
// etc...
}
声明该类是abstract
因为实例化它没有意义。它声明了游戏实体的一些动作和某些属性,但是这些属性在此类中没有初始化。此类用作游戏实体的模板,但并不意味着可以单独实例化,也可以这样声明abstract
。
关于抽象类和接口在用法上的区别:
如我所见,接口是一种获得多态行为而不受某些语言的单一继承机制限制的方法。
让我们以游戏为例。考虑Enemy
从派生的类GameEntity
。此类有一个方法attackMeFromDistance(RangedAttacker attacker)
。此方法旨在允许实体从远处攻击敌人。
如您所见,此方法将RangedAttacker
类型作为参数。但是,所有游戏实体都已经继承自GameEntity
。他们无法扩展其他课程。
就拿类Mage
和Archer
的例子。我们希望允许它们都被接受为attackMeFromDistance(RangedAttacker attacker)
方法中的参数,但它们已经从派生GameEntity
。
为了解决这个问题,我们创建一个新接口:
interface RangedAttacker{
public void attackFromDistance();
}
实现此接口的类必须实现该attackFromDistance()
方法,因此可以确保它具有远程攻击能力。这意味着该attackMeFromDistance
方法现在可以安全地接受实现此接口的类。因此,创建Mage
并Archer
实现该接口可以解决我们的问题。
对我来说,这就是界面的力量。
综上所述,当您想为某些类提供基类时,通常会使用抽象类,但abstract
单独实例化它(或在具有方法的情况下,必须将其实例化)是没有意义的。由子类实现,在这种情况下,编译器将强制您创建类abstract
。您将使用接口来获得多态行为,而不受单一继承机制的限制。