Skip to content

Java 接口

在 Java 中,interface(接口)是一种引用类型,类似于 class(类),它定义了一种契约。它指定了一组实现类必须提供的 abstract method(抽象方法)。从 Java 8 开始,接口还可以包含 default method(默认方法)、static method(静态方法),甚至 private method(私有方法,从 Java 9 开始)。

可以将 interface 视为功能的一种蓝图。class 通过 implements(实现)一个接口,承诺提供该接口契约中定义的功能。

  • 不能直接实例化。
  • 可以包含 abstract method(抽象方法,没有实现,隐式为 public abstract)。
  • 可以包含 default method(默认方法,Java 8 引入):提供一个实现类可以直接继承的默认实现。这允许在接口中添加新方法,而不会破坏现有的实现。
  • 可以包含 static method(静态方法,Java 8 引入):与接口本身关联的工具方法,不与任何实现对象关联。
  • 可以包含 private method(私有方法,Java 9 引入):接口内部默认方法或静态方法的辅助方法。
  • 可以包含 public static final 常量(字段)。所有字段隐式都是 public、static 和 final 的。
  • 一个类可以实现多个接口。
  • 一个接口可以继承(extends)多个其他接口。
  • 实现抽象:隐藏实现细节,仅暴露必要行为。
  • 实现类型的多重继承:一个类可以实现多个接口,继承不同的功能集合。
  • 实现松耦合:代码依赖于接口契约,而不是具体的实现,使系统更灵活且易于维护。
  • 实现多态:对象可以被视为其实现的接口类型的实例。
  • 定义 API:为库和框架定义清晰的契约。

使用 interface 关键字来声明接口。文件名必须与接口名匹配(例如,Vehicle.java)。

/* 文件名: Vehicle.java */
/**
* 定义车辆的基本功能。
*/
public interface Vehicle {
// 常量(隐式为 public static final)
String TYPE = "Generic Vehicle";
// 抽象方法(隐式为 public abstract)
void startEngine();
void stopEngine();
// 默认方法(Java 8+)
default void honk() {
System.out.println("Beep beep!");
log("Honk executed"); // 使用私有辅助方法
}
// 静态方法(Java 8+)
static void displayType() {
System.out.println("This is a type of: " + TYPE);
}
// 私有方法(Java 9+)- 默认方法/静态方法的辅助方法
private void log(String message) {
System.out.println("LOG: " + message);
}
}

一个类使用 implements 关键字来实现一个或多个接口。如果一个类实现了一个接口但没有为所有抽象方法提供实现,则该类本身必须被声明为 abstract(抽象类)。

在实现接口方法时使用 @Override 注解,以便进行编译时检查和提高代码清晰度。

Car.java
public class Car implements Vehicle {
private boolean engineRunning = false;
@Override
public void startEngine() {
engineRunning = true;
System.out.println("Car engine started.");
}
@Override
public void stopEngine() {
engineRunning = false;
System.out.println("Car engine stopped.");
}
// 可选:重写默认方法
@Override
public void honk() {
System.out.println("HONK HONK! (Car specific)");
}
public boolean isEngineRunning() {
return engineRunning;
}
public static void main(String[] args) {
// 使用接口中的静态方法
Vehicle.displayType();
Car myCar = new Car();
myCar.startEngine();
myCar.honk(); // 调用被重写的 honk 方法
myCar.stopEngine();
// 多态:将 Car 视为 Vehicle 类型
Vehicle anotherVehicle = new Car();
anotherVehicle.startEngine();
// 错误:isEngineRunning() 方法不在 Vehicle 接口中
// anotherVehicle.isEngineRunning();
// 如果没有重写 honk,则使用默认 honk 的示例
Vehicle bike = new Motorbike(); // 假设 Motorbike 类实现了 Vehicle 接口但没有重写 honk 方法
bike.startEngine();
bike.honk(); // 调用 Vehicle 接口中的默认 honk() 方法
bike.stopEngine();
}
}
// 假设 Motorbike 类存在,并实现了 Vehicle 接口但没有重写 honk
/*
class Motorbike implements Vehicle {
@Override
public void startEngine() { System.out.println("Motorbike engine started."); }
@Override
public void stopEngine() { System.out.println("Motorbike engine stopped."); }
}
*/
  • 类必须实现其所实现接口中定义的所有抽象方法,否则该类必须声明为 abstract(抽象类)。
  • 实现方法必须具有相同的签名(名称、参数类型)和兼容的返回类型(相同或子类型)。
  • 实现方法不能具有更严格的访问修饰符(例如,如果接口方法是 public,则实现也必须是 public)。
  • 使用 @Override 注解。
  • 一个类可以通过在 implements 关键字后列出多个接口(用逗号分隔)来同时实现多个接口(例如,class MyClass implements InterfaceA, InterfaceB)。

一个接口可以使用 extends 关键字继承(扩展)其他接口。这允许创建接口的层次结构。子接口继承其父接口的所有抽象方法、默认方法、静态方法和常量。

ElectricVehicle.java
public interface ElectricVehicle extends Vehicle { // 继承 Vehicle
void charge();
int getBatteryLevel();
}
// 文件名: Autonomous.java
public interface Autonomous {
void driveAutonomously();
}
// 文件名: AutonomousElectricCar.java
// 实现类需要实现来自 ElectricVehicle、Vehicle 和 Autonomous 的方法
public class AutonomousElectricCar implements ElectricVehicle, Autonomous {
@Override
public void startEngine() { /* 实现 */ System.out.println("Electric motor started."); }
@Override
public void stopEngine() { /* 实现 */ System.out.println("Electric motor stopped."); }
@Override
public void charge() { /* 实现 */ System.out.println("Charging..."); }
@Override
public int getBatteryLevel() { /* 实现 */ return 85; }
@Override
public void driveAutonomously() { /* 实现 */ System.out.println("Driving autonomously."); }
// 它仍然可以使用来自 Vehicle 的默认/静态方法,例如 honk()
}

随着默认方法的加入,接口也可以提供实现了,这使得接口与抽象类之间的界限变得模糊。然而,关键区别依然存在:

  • 多重继承: 一个类可以实现多个接口,但只能继承一个抽象类。
  • 状态(实例变量): 抽象类可以有实例变量(状态),而传统上接口不能(只能有 public static final 常量)。
  • 构造方法: 抽象类可以有构造方法,接口不能。
  • 用途: 当你想要为一组相关的子类提供通用的基础实现和状态时,使用抽象类(一种 “is-a” 关系)。当你想要定义一个可以由各种可能不相关的类实现(提供)的契约或能力时,使用接口(一种 “has-a” 或 “can-do” 关系)。

函数式接口是只包含一个抽象方法的接口(但可以包含多个默认方法或静态方法)。它们对于在 Java 中使用 lambda expression(Lambda 表达式)和 method reference(方法引用)实现函数式编程至关重要(例如 Runnable、Comparator、ActionListener)。

可以使用 @FunctionalInterface 注解来表明这种意图,并让编译器进行验证。

@FunctionalInterface
interface MyFunctionalInterface {
void execute(); // 单个抽象方法
default void print() {
System.out.println("Printing from functional interface.");
}
}
// Lambda 用法:
MyFunctionalInterface runner = () -> System.out.println("Executing lambda.");
runner.execute(); // 输出:Executing lambda.
runner.print(); // 输出:Printing from functional interface.

查阅 Java 文档,了解 List、Map、Set、Comparable、Runnable 和 Callable 等核心接口,以查看实际示例。