在我的一次采访中,我被要求解释接口类和抽象类之间的区别。
以下是我的回答:
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;
}
接口由单例变量(公共静态final)和公共抽象方法组成。当我们知道要做什么但不知道如何做时,我们通常更喜欢实时使用接口。
这个概念可以通过以下例子更好地理解:
考虑一个支付类。支付方式有很多种,比如PayPal,信用卡等。因此,我们通常将Payment作为接口,其中包含makePayment()方法,CreditCard和PayPal是两个实现类。
public interface Payment
{
void makePayment();//by default it is a abstract method
}
public class PayPal implements Payment
{
public void makePayment()
{
//some logic for PayPal payment
//e.g. Paypal uses username and password for payment
}
}
public class CreditCard implements Payment
{
public void makePayment()
{
//some logic for CreditCard payment
//e.g. CreditCard uses card number, date of expiry etc...
}
}
在上面的例子中,CreditCard和PayPal是两个实现类/策略。接口还允许我们在Java中实现多重继承的概念,这是抽象类无法实现的。
当我们知道某些特性该做什么,而其他特性又知道如何执行时,我们就会选择一个抽象类。
考虑下面的例子:
public abstract class Burger
{
public void packing()
{
//some logic for packing a burger
}
public abstract void price(); //price is different for different categories of burgers
}
public class VegBerger extends Burger
{
public void price()
{
//set price for a veg burger.
}
}
public class NonVegBerger extends Burger
{
public void price()
{
//set price for a non-veg burger.
}
}
如果我们将来在给定的抽象类中添加方法(具体的/抽象的),那么实现类将不需要更改其代码。但是,如果将来在接口中添加方法,则必须将实现添加到实现该接口的所有类中,否则会发生编译时错误。
还有其他不同之处,但这些都是面试官所期望的。希望这对你们有帮助。
你的所有语句都是有效的,除了你的第一个语句(在Java 8发布之后):
Java接口的方法是隐式抽象的,不能有实现
从文档页:
接口是一种引用类型,类似于类,只能包含
常量、方法签名、默认方法、静态方法和嵌套类型
方法体只存在于默认方法和静态方法中。
默认的方法:
接口可以有默认方法,但与抽象类中的抽象方法不同。
默认方法使您能够向库的接口添加新功能,并确保与为这些接口的旧版本编写的代码的二进制兼容性。
当您扩展一个包含默认方法的接口时,您可以执行以下操作:
完全不要提及默认方法,这将使您的扩展接口继承默认方法。
重新声明默认方法,使其抽象。
重新定义默认方法,该方法将覆盖它。
静态方法:
除了默认方法外,还可以在接口中定义静态方法。静态方法是与定义它的类相关联的方法,而不是与任何对象相关联的方法。类的每个实例都共享它的静态方法。)
这让你更容易在你的库中组织帮助方法;
来自文档页的关于接口具有静态和默认方法的示例代码。
import java.time.*;
public interface TimeClient {
void setTime(int hour, int minute, int second);
void setDate(int day, int month, int year);
void setDateAndTime(int day, int month, int year,
int hour, int minute, int second);
LocalDateTime getLocalDateTime();
static ZoneId getZoneId (String zoneString) {
try {
return ZoneId.of(zoneString);
} catch (DateTimeException e) {
System.err.println("Invalid time zone: " + zoneString +
"; using default time zone instead.");
return ZoneId.systemDefault();
}
}
default ZonedDateTime getZonedDateTime(String zoneString) {
return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
}
}
使用下面的指导原则来选择是使用接口还是抽象类。
接口:
定义一个契约(最好是无状态的——我的意思是没有变量)
将不相关的类链接到具有一种功能。
声明公共常量变量(不可变状态)
抽象类:
在几个密切相关的类之间共享代码。它建立了一种关系。
在相关类之间共享公共状态(状态可以在具体类中修改)
相关文章:
接口与抽象类(通用面向对象)
实现和扩展:什么时候使用?有什么不同?
通过这些例子,你可以理解
不相关的类可以通过接口具有功能,但相关类通过扩展基类来改变行为。