作为一个 Android 开发初学者,每当别人提到“接口”这个词时,我内心总会毫不在意地想:“接口?不就是 implements 一下就完事了吗?”然而,真正开始做项目后才发现,原来接口的使用远不止定义几个方法那么简单。很多关于接口的应用技巧和设计思想,在实际开发中常常被我忽略。你是否也曾遇到过类似的情况呢?让我们一起来深入探讨一下吧!
一.通过接口使用实现类的对象
通过接口使用实现类的对象体现的是多态的理念,他的核心是面向接口编程.实现类必须实现接口中定义的所有抽象方法(除非该类是抽象类).
-
基本语法结构
1
2
3接口名 变量名 = new 实现类构造器();
List<String> list = new ArrayList<>();
list.add("小明");这行代码的意思是:
- 左边:接口 List —— 表示你“只关心行为”(比如添加、删除、查找等列表操作).
- 右边:实现类 ArrayList —— 提供具体的行为实现.
- 变量名 list —— 是你手中握住的“遥控器”,通过变量名你可以使用对应的方法.
这就是通过接口来使用实现类对象的经典方式.
-
设计思想
编程时面向接口,而非面向实现。
松耦合:更容易替换实现类
可扩展:可以随时替换为
LinkedList、CopyOnWriteArrayList等实现易测试:Mock 或 stub 方便替换实现
更清晰的 API 设计:只暴露该暴露的能力
-
自定义接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// 1. 定义接口
public interface Animal {
void makeSound();
}
// 2. 实现类
public class Dog implements Animal {
public void makeSound() {
System.out.println("汪汪!");
}
}
// 3. 使用接口引用实现类对象
public class Test {
public static void main(String[] args) {
Animal a = new Dog(); // 接口引用指向实现类
a.makeSound(); // 输出:汪汪!
}
}虽然我们拿的是
Animal类型的遥控器,但背后控制的是Dog这台机器。
二.接口作为参数或返回值
“接口作为参数或返回值”"是 Android 和 Java/Kotlin 编程中非常关键的一项技能,尤其是在 回调机制、事件处理、模块解耦 等场景中应用极广。
核心概念:
| 类型 | 说明 |
|---|---|
| 作为参数 | 把某个接口实例传入函数中,用来回调或配置行为 |
| 作为返回值 | 函数返回一个接口实例,可以由调用者接收并使用 |
一、接口作为参数(监听按钮点击后执行某个回调逻辑)
定义一个接口:
1 | interface OnConfirmListener { |
函数接受这个接口作为参数:
1 | fun showDialog(listener: OnConfirmListener) { |
listener.onConfirm(): 这是关键所在。showDialog 函数通过其接收到的 listener 参数(类型是接口),调用了接口中定义的 onConfirm() 方法。此时,showDialog 函数并不知道 listener 参数具体是哪个类的实例,它只知道这个实例有一个 onConfirm() 方法可以被调用。这正是多态的体现。
使用接口对象传递行为:
1 | showDialog(object : OnConfirmListener { |
showDialog(...): 这里调用了 showDialog 函数。
object : OnConfirmListener { ... }: 这是 Kotlin 中的匿名对象 (Anonymous Object)。它是一个临时的、只使用一次的类实例,并且直接实现了 OnConfirmListener 接口。
override fun onConfirm() { ... }: 在匿名对象内部,我们提供了 onConfirm() 方法的具体实现,也就是当确认事件发生时,我们希望执行的逻辑(例如,“执行确认操作,比如提交表单”)。
目的: 通过这种方式,我们将一个“行为”(onConfirm() 的实现)作为参数传递给了 showDialog 函数。showDialog 在合适的时机(模拟用户点击确认后)会“回调”这个行为。
Kotlin 中还可以使用 Lambda 简化(如果是 SAM 接口):
1 | fun showDialog(onConfirm: () -> Unit) { |
fun showDialog(onConfirm: () -> Unit): 这是 Kotlin 的一个强大特性:如果一个接口只包含一个抽象方法(称为 SAM - Single Abstract Method 接口),Kotlin 允许你使用 Lambda 表达式 来简化它的实现。这里的 onConfirm: () -> Unit 表示 onConfirm 是一个函数类型参数,它接受零个参数并返回 Unit(相当于 Java 中的 void)。
onConfirm(): 调用传入的 Lambda 表达式。
showDialog { println("Lambda方式的确认逻辑") }: 这是 Lambda 表达式的调用方式。它提供了一种更简洁、更具函数式编程风格的方式来实现回调。当函数类型参数是最后一个参数时,Kotlin 允许将 Lambda 放在圆括号外部(尾随 Lambda)。
二、接口作为返回值(你希望从某个类中获取“策略”或“处理逻辑”)
定义接口:
1 | interface DataProcessor { |
函数返回接口对象:
1 | fun getProcessor(): DataProcessor { |
fun getProcessor(): DataProcessor: 定义了一个名为 getProcessor 的函数,它声明返回一个 DataProcessor 类型的对象。
return object : DataProcessor { ... }: 这里,getProcessor 函数返回了一个匿名对象,这个匿名对象实现了 DataProcessor 接口。
override fun process(data: String) { println("处理数据:$data") }: 这是匿名对象内部对 process 方法的具体实现。这个实现就是 getProcessor 函数所提供的“处理逻辑”。
调用方使用返回的接口对象:
1 | val processor = getProcessor() |
val processor = getProcessor(): 调用 getProcessor() 函数,并将返回的 DataProcessor 接口对象赋值给 processor 变量。注意,processor 变量的静态类型是 DataProcessor 接口类型。
processor.process("Hello from interface!"): 通过 processor 接口变量,调用了它所引用的实现类对象中的 process 方法。此时,执行的是 getProcessor 函数内部匿名对象里定义的 process 逻辑。
三.接口的默认方法与函数式接口
你问到了 Java 接口中两个非常现代且实用的特性:默认方法(default methods) 与 函数式接口(functional interfaces)。这两个概念如同接口的“双翼”,一个重在扩展性,一个重在简洁性,帮助接口在面向对象与函数式编程之间架起桥梁。
一、接口的默认方法(Default Method)
定义
Java 8 开始,接口中允许定义带有默认实现的方法,用 default 关键字修饰。
1 | public interface Animal { |
示例
1 | public class Dog implements Animal { |
二、函数式接口(Functional Interface)
定义
函数式接口是只包含一个抽象方法的接口,适合用于 Lambda 表达式。
使用 @FunctionalInterface 注解可明确声明该意图:
1 |
|
示例:使用 Lambda 表达式
1 | public class Main { |
| 特性 | 接口默认方法 | 函数式接口(Functional Interface) | 接口多态(通过接口使用实现类的对象) | 接口作为参数或返回值 |
|---|---|---|---|---|
| 出现版本 | Java 8 | Java 8 | Java 基础特性 | Java 基础特性 |
| 方法数量 | 可有多个方法(含默认实现) | 只能有一个抽象方法 | 接口中定义的所有抽象方法 | 接口中定义的抽象方法(可含默认方法) |
| 使用关键字 | default |
@FunctionalInterface(建议加) |
interface、implements |
interface + 参数/返回类型为接口 |
| 可否有默认方法 | ✅ 可以 | ✅ 可以 | ✅ 可以(Java 8 起) | ✅ 可以(Java 8 起) |
| 使用优势 | 向后兼容,减少重复逻辑 | 适配 Lambda,简洁表达函数逻辑 | 解耦结构、支持多态、便于扩展与维护 | 增强灵活性、支持回调、适配不同实现类 |
| 使用场景 | 接口升级、代码复用 | Lambda 表达式、方法引用、回调、流式操作等 | 多态调用、策略模式、面向接口设计、依赖注入等 | 回调机制、策略模式、工厂方法、依赖注入等 |
| 表现形式 | 在接口中用 default 修饰方法 |
用 Lambda 表达式实现:(a, b) -> a + b |
接口名 obj = new 实现类(); |
void func(接口名 obj) / 接口名 getObj() |
通过上面的内容,我们一起回顾了在 Android 实际项目中常见的三个接口使用方式和一些实用技巧。接口虽然简单,但灵活运用起来却大有学问。希望这些内容能在你今后的开发过程中带来启发和帮助!
说些什么吧!