一、接口(Interface)的用途和核心思想
接口的核心思想是:定义一套行为规范或契约。它只规定“应该做什么”(方法声明),但不关心“具体怎么做”(方法实现)。
其主要用途可以概括为:
- 实现多态(Polymorphism): 这是接口最重要的用途。一个类可以实现多个接口,从而可以被当做不同的类型来使用,极大地增加了代码的灵活性。
- 实现完全抽象: 在 Java 8 之前,接口是所有方法都是抽象且未实现的纯粹抽象类。它强制实现类提供所有方法的具体实现,确保了规范的执行。
- 降低耦合(Decoupling): 代码依赖于接口(抽象),而不是具体的实现类。这使得你可以轻松替换不同的实现,而不影响其他部分的代码。这是软件设计原则(如依赖倒置原则)的核心。
- 定义回调机制(Callback): 正如监听器(Listener)所示,接口是实现回调的完美工具。你提供一个接口,别人(系统或其他类)会在特定时机调用你实现的方法。
二、关键特性和语法演变(Java 7 → Java 8+)
接口的特性随着 Java 版本在不断演进,这在 Android 开发中(取决于你设置的 compileSdkVersion
和 targetSdkVersion
)同样适用。
Java 7 及以前(传统接口)
抽象方法(Abstract Methods): 接口中的方法默认是
public abstract
的,不能有方法体。实现类必须重写所有这些方法。public interface Animal { void eat(); // 默认就是 public abstract void eat(); void sleep(); // 默认就是 public abstract void sleep(); }
常量(Constants): 接口中定义的变量默认是
public static final
的(即常量)。public interface Constants { int MAX_SPEED = 100; // 默认就是 public static final int MAX_SPEED = 100; }
Java 8 及以后(现代接口)
为了增强接口的灵活性,Java 8 引入了两个重要的新特性:
默认方法(Default Methods): 使用
default
关键字修饰的方法。它可以有方法体。- 用途: 当需要为接口添加新方法时,为了避免破坏所有已有的实现类,可以提供一個默认实现。这样已有的实现类无需做任何修改。
public interface Vehicle { void start(); // 传统抽象方法 // Java 8 默认方法 default void honk() { System.out.println("Beep beep!"); } } // 实现类可以选择不重写 honk(),直接使用默认实现 public class Car implements Vehicle { @Override public void start() { System.out.println("Car starts with key."); } // 没有重写 honk(),使用 Vehicle 接口的默认实现 }
静态方法(Static Methods): 使用
static
关键字修饰的方法。它属于接口本身,而不是实现类的实例。通过接口名直接调用。- 用途: 提供与接口相关的工具方法,这些方法不需要依赖对象实例。
public interface MathOperations { static int add(int a, int b) { return a + b; } } // 调用方式:MathOperations.add(5, 3); // 输出 8
三、主要使用场景
1. 定义回调机制 / 监听器模式 (Most Common in Android)
这是 Android 开发中最常见的接口用法。
// 1. 定义回调接口
public interface OnDownloadCompleteListener {
void onComplete(String filePath);
void onError(Exception e);
}
// 2. 在一个服务类中持有接口引用并提供设置方法
public class DownloadManager {
private OnDownloadCompleteListener mListener;
public void setOnDownloadCompleteListener(OnDownloadCompleteListener listener) {
this.mListener = listener;
}
public void startDownload(String url) {
// 模拟下载过程...
new Thread(() -> {
try {
// ... 下载逻辑
// 3. 在完成后回调
if (mListener != null) {
mListener.onComplete("/sdcard/downloaded_file.zip");
}
} catch (Exception e) {
if (mListener != null) {
mListener.onError(e);
}
}
}).start();
}
}
// 4. 在Activity中实现接口并设置监听器
public class MainActivity extends AppCompatActivity implements OnDownloadCompleteListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DownloadManager manager = new DownloadManager();
manager.setOnDownloadCompleteListener(this); // 设置监听器
manager.startDownload("http://example.com/file.zip");
}
// 5. 实现回调方法
@Override
public void onComplete(String filePath) {
runOnUiThread(() -> Toast.makeText(this, "Downloaded: " + filePath, Toast.LENGTH_SHORT).show());
}
@Override
public void onError(Exception e) {
runOnUiThread(() -> Toast.makeText(this, "Error: " + e.getMessage(), Toast.LENGTH_SHORT).show());
}
}
2. 实现多态和策略模式
根据不同情况使用不同的算法或策略。
// 定义策略接口
public interface PaymentStrategy {
void pay(double amount);
}
// 实现不同的策略
public class CreditCardPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " using Credit Card.");
}
}
public class PayPalPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " using PayPal.");
}
}
// 使用策略的上下文类
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(double amount) {
paymentStrategy.pay(amount);
}
}
// 使用
ShoppingCart cart = new ShoppingCart();
cart.setPaymentStrategy(new CreditCardPayment()); // 可以轻松替换策略
cart.checkout(100.0);
3. 依赖抽象,而非具体实现(降低耦合)
这是优秀架构设计的关键。
// 接口(抽象)
public interface UserRepository {
User getUserById(int id);
void saveUser(User user);
}
// 具体实现1:数据库存储
public class DatabaseUserRepository implements UserRepository {
@Override
public User getUserById(int id) { /* SQLite 查询逻辑 */ }
@Override
public void saveUser(User user) { /* SQLite 插入逻辑 */ }
}
// 具体实现2:网络API存储
public class ApiUserRepository implements UserRepository {
@Override
public User getUserById(int id) { /* 网络请求逻辑 */ }
@Override
public void saveUser(User user) { /* 网络请求逻辑 */ }
}
// 业务逻辑类,它只依赖接口,不关心具体实现
public class UserManager {
private final UserRepository repository; // 依赖接口
// 通过构造函数注入依赖(Dependency Injection)
public UserManager(UserRepository repo) {
this.repository = repo;
}
public void updateUserProfile(User user) {
// 业务逻辑...
repository.saveUser(user); // 调用接口方法,不关心是存到数据库还是网络
}
}
// 这样,测试时你可以传入一个 Mock 实现,非常方便。
四、接口 vs. 抽象类
这是一个经典面试题。它们很相似,但有关键区别:
特性 | 接口 (Interface) | 抽象类 (Abstract Class) |
---|---|---|
方法实现 | Java 8 前不能有,之后可以有默认/静态方法 | 可以有抽象方法,也可以有具体实现的方法 |
成员变量 | 只能是常量 (public static final ) |
可以是普通变量、常量、静态变量 |
构造方法 | 没有构造方法 | 有构造方法(虽然不能实例化,但子类可以调用) |
继承 | 一个类可以实现多个接口 | 一个类只能继承一个抽象类 |
设计目的 | “has-a” 关系,定义行为契约、能力 | “is-a” 关系,定义是什么,提供模板 |
访问修饰符 | 方法默认 public |
方法可以有 public , protected , private |
如何选择?
- 如果你主要关心的是定义一组行为或能力,并且不相关的类可能需要共享这些行为,使用接口。(例如:
Comparable
(可比较),Serializable
(可序列化))。 - 如果你要定义一些紧密相关的对象的基本模板,并且其中包含一些公共的实现代码,使用抽象类。(例如:
Animal
作为Dog
,Cat
的基类)。
总结
接口是 Java/Android 实现抽象、多态和低耦合代码的最强大工具之一。
- 用途: 定义契约、实现多态、解耦代码、实现回调。
- 核心方法: 抽象方法(必须实现)、默认方法(可选实现)、静态方法(接口工具方法)。
- 主要场景:
- Android 监听器/回调 (最常见)
- 策略模式等设计模式
- 依赖注入和架构设计(如 Repository 模式)
- 关键优势: 一个类可以实现多个接口,提供了比继承更灵活的代码复用方式。