Using an Interface vs. Abstract Class in Java – 在Java中使用接口与抽象类

最后修改: 2021年 5月 13日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

Abstraction is one of the Object-Oriented programming key features. It allows us to hide the implementation complexities just by providing functionalities via simpler interfaces. In Java, we achieve abstraction by using either an interface or an abstract class.

抽象是面向对象编程的关键特征之一。它允许我们通过更简单的接口来提供功能,从而隐藏实现的复杂性。在Java中,我们通过使用interfaceabstract class来实现抽象。

In this article, we’ll discuss when to use an interface and when to use an abstract class while designing applications. Also, the key differences between them and which one to choose based on what we’re trying to achieve.

在这篇文章中,我们将讨论在设计应用程序时何时使用接口,何时使用抽象类。还有,它们之间的主要区别,以及根据我们要实现的目标选择哪一个。

2. Class vs. Interface

2.类与界面

First, let’s look at the differences between a normal concrete class vs. an interface.

首先,让我们看看普通的具体类与接口的区别。

A class is a user-defined type that acts as a blueprint for object creation. It can have properties and methods that represent the states and behaviors of an object, respectively.

类是一种用户定义的类型,作为创建对象的蓝图。它可以有属性和方法,分别代表一个对象的状态和行为。

An interface is also a user-defined type that is syntactically similar to a class. It can have a collection of field constants and method signatures that will be overridden by interface implementing classes.

接口也是一种用户定义的类型,在语法上类似于类。它可以有一个字段常量和方法签名的集合,将被接口实现类重写。

In addition to these, Java 8 new features support static and default methods in interfaces to support backward compatibility. Methods in an interface are implicitly abstract if they are not static or default and all are public.

除此以外,Java 8 新特性支持接口中的静态和默认方法,以支持向后兼容。如果一个接口中的方法不是staticdefault,并且都是public,那么这些方法就是隐含的抽象的。

However, starting with Java 9, we can also add private methods in interfaces.

然而,从Java 9开始,我们也可以在接口中添加private方法

3. Interface vs. Abstract Class

3.接口与抽象类

An abstract class is nothing but a class that is declared using the abstract keyword. It also allows us to declare method signatures using the abstract keyword (abstract method) and forces its subclasses to implement all the declared methods. Suppose if a class has a method that is abstract, then the class itself must be abstract.

一个抽象类只不过是一个使用abstract关键字声明的类。它还允许我们使用abstract关键字(抽象方法)来声明方法签名,并强制其子类实现所有声明的方法。假设如果一个类有一个方法是抽象的,那么这个类本身也必须是抽象的。

Abstract classes have no restrictions on field and method modifiers, while in an interface, all are public by default. We can have instance and static initialization blocks in an abstract class, whereas we can never have them in the interface. Abstract classes may also have constructors which will get executed during the child object’s instantiation.

抽象类对字段和方法修饰符没有限制,而在接口中,所有的字段和方法修饰符都默认为公共的。我们可以在抽象类中拥有实例和静态初始化块,而在接口中我们永远不能拥有它们。抽象类也可以有构造器,这些构造器将在子对象的实例化过程中得到执行。

Java 8 introduced functional interfaces, an interface with a restriction of no more than one declared abstract method. Any interface with a single abstract method other than static and default methods is considered a functional interface. We can use this feature to restrict the number of abstract methods to be declared. While in abstract classes, we can never have this restriction on the number of abstract methods declaration.

Java 8 引入了功能接口,这种接口的限制是不能有超过一个已声明的抽象方法。除了静态方法和默认方法之外,任何具有单一抽象方法的接口都被认为是功能性接口。我们可以利用这个特点来限制申报的抽象方法的数量。而在抽象类中,我们永远不可能对抽象方法的声明数量有这种限制。

Abstract classes are analogous to interfaces in some ways:

抽象类在某些方面类似于接口。

  • We can’t instantiate either of them. i.e., we cannot use the statement new TypeName() directly to instantiate an object. If we used the aforementioned statement, we have to override all the methods using an anonymous class
  • They both might contain a set of methods declared and defined with or without their implementation. i.e., static & default methods(defined) in an interface, instance methods(defined) in abstract class, abstract methods(declared) in both of them

4. When to Use an Interface

4.何时使用一个接口

Let’s look at some scenarios when one should go with an interface:

让我们来看看在哪些情况下应该使用接口。

  • If the problem needs to be solved using multiple inheritances and is composed of different class hierarchies
  • When unrelated classes implement our interface. For example, Comparable provides the compareTo() method that can be overridden to compare two objects
  • When application functionalities have to be defined as a contract, but not concerned about who implements the behavior. i.e., third-party vendors need to implement it fully

Consider using the interface when our problem makes the statement “A is capable of [doing this]”. For example, “Clonable is capable of cloning an object”, “Drawable is capable of drawing a shape”, etc.

当我们的问题做出 “A能够[这样做]”的陈述时,请考虑使用接口。例如,”Clonable能够克隆一个对象”,”Drawable能够绘制一个形状”,等等。

Let us consider an example that makes use of an interface:

让我们考虑一个利用接口的例子。

public interface Sender {
    void send(File fileToBeSent);
}
public class ImageSender implements Sender {
    @Override
    public void send(File fileToBeSent) {
        // image sending implementation code.
    }
}

Here, Sender is an interface with a method send(). Hence, “Sender is capable of sending a file” we implemented it as an interface. ImageSender implements the interface for sending an image to the target. We can further use the above interface to implement VideoSender, DocumentSender to accomplish various jobs.

这里,Sender是一个接口,有一个方法send()。因此,”发送者能够发送一个文件”,我们把它作为一个接口来实现。ImageSender实现了向目标发送图像的接口。我们可以进一步使用上述接口来实现VideoSenderDocumentSender来完成各种工作。

Consider a unit test case the makes use of the above interface and its implemented class:

考虑一个使用上述接口和其实现类的单元测试案例。

@Test
void givenImageUploaded_whenButtonClicked_thenSendImage() { 
 
    File imageFile = new File(IMAGE_FILE_PATH);
 
    Sender sender = new ImageSender();
    sender.send(imageFile);
}

5. When to Use an Abstract Class

5.何时使用抽象类

Now, let’s see some scenarios when one should use the abstract class:

现在,让我们看看一些应该使用抽象类的情况。

  • When trying to use the inheritance concept in code (share code among many related classes), by providing common base class methods that the subclasses override
  • If we have specified requirements and only partial implementation details
  • While classes that extend abstract classes have several common fields or methods (that require non-public modifiers)
  • If one wants to have non-final or non-static methods to modify the states of an object

Consider using abstract classes and inheritance when our problem makes the evidence “A is a B”. For example, “Dog is an Animal”, “Lamborghini is a Car”, etc.

当我们的问题做出 “A是B “的证据时,考虑使用抽象类和继承。例如,”狗是一种动物”,”兰博基尼是一种汽车”,等等。

Let’s look at an example that uses the abstract class:

我们来看看一个使用抽象类的例子。

public abstract class Vehicle {
    
    protected abstract void start();
    protected abstract void stop();
    protected abstract void drive();
    protected abstract void changeGear();
    protected abstract void reverse();
    
    // standard getters and setters
}
public class Car extends Vehicle {

    @Override
    protected void start() {
        // code implementation details on starting a car.
    }

    @Override
    protected void stop() {
        // code implementation details on stopping a car.
    }

    @Override
    protected void drive() {
        // code implementation details on start driving a car.
    }

    @Override
    protected void changeGear() {
        // code implementation details on changing the car gear.
    }

    @Override
    protected void reverse() {
        // code implementation details on reverse driving a car.
    }
}

In the above code, the Vehicle class has been defined as abstract along with other abstract methods. It provides generic operations of any real-world vehicle and also has several common functionalities. The Car class, which extends the Vehicle class, overrides all the methods by providing the car’s implementation details (“Car is a Vehicle”).

在上述代码中,Vehicle类和其他抽象方法一起被定义为抽象的。它提供了任何现实世界中的车辆的通用操作,也有几个共同的功能。Car类扩展了Vehicle类,通过提供汽车的实现细节(”Car is a Vehicle”)重写了所有的方法。

Hence, we defined the Vehicle class as abstract in which the functionalities can be implemented by any individual real vehicle like cars and buses. For example, in the real world, starting a car and bus is never going to be the same (each of them needs different implementation details).

因此,我们将Vehicle类定义为抽象的,其中的功能可以由任何单独的真实车辆如汽车和公共汽车来实现。例如,在现实世界中,汽车和公共汽车的启动是永远不会相同的(它们各自需要不同的实现细节)。

Now, let’s consider a simple unit test that makes use of the above code:

现在,让我们考虑一个利用上述代码的简单单元测试。

@Test
void givenVehicle_whenNeedToDrive_thenStart() {

    Vehicle car = new Car("BMW");

    car.start();
    car.drive();
    car.changeGear();
    car.stop();
}

6. Conclusion

6.结语

This article discussed the overview of interfaces and abstract classes and the key differences between them. Also, we examined when to use each of them in our work to accomplish writing flexible and clean code.

本文讨论了接口和抽象类的概况以及它们之间的主要区别。此外,我们还研究了在我们的工作中何时使用它们中的每一种,以完成编写灵活而简洁的代码。

The complete source code for the examples given in this article is available over on GitHub.

本文给出的例子的完整源代码可在GitHub上获得超过