在我的一次采访中,我被要求解释接口类和抽象类之间的区别。
以下是我的回答:
Methods of a Java interface are implicitly abstract
and cannot have implementations. A Java abstract class can have
instance methods that implements a default behaviour.
Variables declared in a Java interface are by default final. An
abstract class may contain non-final variables.
Members of a Java interface are public by default. A Java abstract
class can have the usual flavours of class members like private,
protected, etc.
A Java interface should be implemented using keyword “implements”; A
Java abstract class should be extended using keyword “extends”.
An interface can extend another Java interface only, an abstract class
can extend another Java class and implement multiple Java interfaces.
A Java class can implement multiple interfaces but it can extend only
one abstract class.
然而,面试官并不满意,他告诉我这种描述代表了“书本知识”。
他让我给出一个更实际的回答,用实际的例子解释我什么时候会选择抽象类而不是接口。
我哪里错了?
接口是一个“契约”,其中实现契约的类承诺实现方法。举个例子,当我将一款游戏从2D升级到3D时,我不得不编写一个界面而不是类。我必须创建一个界面来共享2D和3D版本的游戏类别。
package adventure;
import java.awt.*;
public interface Playable {
public void playSound(String s);
public Image loadPicture(String s);
}
然后我可以实现基于环境的方法,同时仍然能够从一个不知道正在加载的游戏版本的对象调用这些方法。
公共类Adventure扩展了JFrame实现了Playable
公共类Dungeon3D扩展了SimpleApplication实现的Playable
公共类Main扩展了SimpleApplication实现了AnimEventListener
ActionListener,播放
通常,在游戏世界中,世界可以是一个抽象类,在游戏中执行方法:
public abstract class World...
public Playable owner;
public Playable getOwner() {
return owner;
}
public void setOwner(Playable owner) {
this.owner = owner;
}
我将尝试用实际场景来回答,以说明两者之间的区别。
接口是零负载的,即不需要维护状态,因此将契约(能力)与类关联是更好的选择。
例如,说我有一个执行一些操作的任务类,现在在单独的线程中执行一个任务,我不需要扩展线程类,更好的选择是使任务实现可运行的接口(即实现其run()方法),然后将此任务类的对象传递给线程实例并调用其start()方法。
现在你可以问,如果Runnable是一个抽象类呢?
从技术上讲,这是可能的,但从设计角度来看,这是一个糟糕的选择原因:
Runnable没有与之相关的状态,也没有“提供”任何状态
run()方法的默认实现
Task必须扩展它,因此它不能扩展任何其他类
Task没有提供任何专门化到Runnable类,它所需要的只是重写run()方法
换句话说,Task类需要在线程中运行的能力,这是通过实现Runnable接口而实现的,而扩展thread类则使其成为线程。
简单地把我们的接口定义为一种能力(契约),而使用
的抽象类,用于定义的框架(公共/部分)实现
它。
免责声明:下面是愚蠢的例子,尽量不要判断:-P
interface Forgiver {
void forgive();
}
abstract class GodLike implements Forgiver {
abstract void forget();
final void forgive() {
forget();
}
}
现在你可以选择成为神一样的人,但你可以选择只成为宽恕者(即不成为神一样的人),并做:
class HumanLike implements Forgiver {
void forgive() {
// forgive but remember
}
}
或者你可以选择像上帝一样去做:
class AngelLike extends GodLike {
void forget() {
// forget to forgive
}
}
P.S.与java 8接口也可以有静态以及默认(可重写实现)方法,因此区别b/w接口和抽象类甚至更窄。
嗯,现在人们渴望实用的方法,你说得很对,但大多数面试官看起来是按照他们目前的要求,想要一个实用的方法。
回答完你的问题后,你应该跳到例子上:
文摘:
例如,我们有一个工资函数,它有一些对所有员工共同的参数。然后我们可以有一个叫做CTC的抽象类,它带有部分定义的方法体,它将被所有类型的员工扩展,并根据他们的额外工资得到补偿。
对于公共功能。
public abstract class CTC {
public int salary(int hra, int da, int extra)
{
int total;
total = hra+da+extra;
//incentive for specific performing employee
//total = hra+da+extra+incentive;
return total;
}
}
class Manger extends CTC
{
}
class CEO extends CTC
{
}
class Developer extends CTC
{
}
接口
Java中的接口允许在不扩展接口功能的情况下拥有接口功能,你必须清楚你想在应用程序中引入的功能签名的实现。它会迫使你做出定义。
不同的功能。
public interface EmployeType {
public String typeOfEmployee();
}
class ContarctOne implements EmployeType
{
@Override
public String typeOfEmployee() {
return "contract";
}
}
class PermanentOne implements EmployeType
{
@Override
public String typeOfEmployee() {
return "permanent";
}
}
通过将methgos定义为抽象类,你也可以对抽象类进行这样的强制活动,现在一个扩展抽象类的类仍然是抽象类,直到它覆盖抽象函数。