在我的一次采访中,我被要求解释接口类和抽象类之间的区别。

以下是我的回答:

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.

然而,面试官并不满意,他告诉我这种描述代表了“书本知识”。

他让我给出一个更实际的回答,用实际的例子解释我什么时候会选择抽象类而不是接口。

我哪里错了?


当前回答

在Java中选择接口是为了避免多重继承中的菱形问题。

如果你想让你所有的方法都由你的客户端实现,你可以选择接口。这意味着你要抽象地设计整个应用程序。

如果您已经知道它们的共同点,那么您就可以选择抽象类。例如,取一个抽象类Car。在更高的级别上实现常见的car方法,如calculateRPM()。这是一种常见的方法,你让客户端实现他自己的行为,比如 calculateMaxSpeed()等。也许你会举几个你在日常工作中遇到的真实例子来解释。

其他回答

你的所有语句都是有效的,除了你的第一个语句(在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));
    }
}

使用下面的指导原则来选择是使用接口还是抽象类。

接口:

定义一个契约(最好是无状态的——我的意思是没有变量) 将不相关的类链接到具有一种功能。 声明公共常量变量(不可变状态)

抽象类:

在几个密切相关的类之间共享代码。它建立了一种关系。 在相关类之间共享公共状态(状态可以在具体类中修改)

相关文章:

接口与抽象类(通用面向对象)

实现和扩展:什么时候使用?有什么不同?

通过这些例子,你可以理解

不相关的类可以通过接口具有功能,但相关类通过扩展基类来改变行为。

我相信面试官想要了解的可能是界面和实现之间的区别。

代码模块的接口——不是Java接口,更通用的说法是“接口”——基本上是与使用该接口的客户端代码之间的契约。

代码模块的实现是使模块工作的内部代码。通常,您可以以多种不同的方式实现特定的接口,甚至可以在客户机代码不知道更改的情况下更改实现。

A Java interface should only be used as an interface in the above generic sense, to define how the class behaves for the benefit of client code using the class, without specifying any implementation. Thus, an interface includes method signatures - the names, return types, and argument lists - for methods expected to be called by client code, and in principle should have plenty of Javadoc for each method describing what that method does. The most compelling reason for using an interface is if you plan to have multiple different implementations of the interface, perhaps selecting an implementation depending on deployment configuration.

A Java abstract class, in contrast, provides a partial implementation of the class, rather than having a primary purpose of specifying an interface. It should be used when multiple classes share code, but when the subclasses are also expected to provide part of the implementation. This permits the shared code to appear in only one place - the abstract class - while making it clear that parts of the implementation are not present in the abstract class and are expected to be provided by subclasses.

根据我的理解和我的做法,

接口就像一个规范/契约,任何实现接口类的类都必须实现抽象类中定义的所有方法(除了默认方法(在Java 8中引入))

然而,当我知道类的一些方法需要实现时,我定义了一个类抽象,而有些方法我仍然不知道实现是什么(我们可能知道函数签名,但不知道实现)。我这样做是为了在开发的后期,当我知道如何实现这些方法时,我可以扩展这个抽象类并实现这些方法。

注意:接口方法中不能有函数体,除非该方法是静态的或默认的。

抽象类不是纯粹的抽象,因为它是具体方法(已实现方法)和未实现方法的集合。 但 接口是纯抽象的,因为只有未实现的方法,没有具体的方法。

为什么是抽象类?

如果用户想为所有对象编写通用功能。 抽象类是未来重新实现的最佳选择,可以在不影响最终用户的情况下增加更多的功能。

为什么接口?

如果用户想要编写不同的功能,那将是对象上的不同功能。 一旦接口发布,如果不需要修改需求,接口是最好的选择。

这里似乎已经涵盖了几乎所有的东西。在抽象类的实际实现上再补充一点:

Abstract关键字也用于防止类被实例化。如果你有一个具体的类,你不想被实例化-让它抽象。