rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • SAM

  • 概念
    • 1. SAM(Single Abstract Method)
    • 2. 函数式接口(Functional Interface)
    • 3. 函数类型(Function Type)
  • 极简记忆
  • SAM、函数类型 和 函数式接口 是什么关系?
  • 三者真正的关系(核心图)
    • 关系拆解(非常关键)
      • 1. 函数式接口 必须满足 SAM
      • 2. 函数类型 和 SAM 无关
      • 3. SAM 转换 = 把函数类型 / lambda → 自动变成 函数式接口
  • 除了函数式接口符合sam 还有哪些符合SAM?
    • 到底什么才算 SAM?
  • 常见符合 SAM 的接口(不全是“函数式接口”)
    • 1. Java 标准库里大量普通接口
    • 2. Android 里各种监听接口
    • 3. 你自己写的普通单方法接口
  • 哪些符合【SAM 规则】?
  • 哪些是【函数式接口】?
  • 除了 FunctionN,还有哪些是【函数式接口】?
    • 1. Java 标准库自带(大量!)
    • 2. Kotlin 你自己写的(fun interface)
    • 3. Kotlin 内置 FunctionN(你已经知道)
  • 除了函数式接口,还有哪些符合 SAM 规则?
    • 1. Java 普通单方法接口(无注解)
    • 2. Android 里大量监听(全是 SAM)
    • 3. Kotlin 普通单方法接口(不加 fun)
  • SAM和函数式接口最清晰分类(背这个就够)
    • 【函数式接口】(小圈)
    • 【SAM 接口】(大圈)
  • 哪些不算 SAM?
  • 有默认方法、静态方法的函数式接口符合 SAM 的例子
    • 1. Java 标准库自带的经典例子
      • ① Comparator<T>(最经典)
      • ② Iterable 的迭代器相关不算,但看这个:Consumer<T>
    • 2. 你自己写一个:带默认 + 静态方法的 SAM 接口
    • 3. Kotlin 版本:fun interface + 默认方法
    • 4. 再给一个 Android 里常见的
  • 函数式接口 底层是什么?
    • 1. 先看最直观的结论
    • 2. 但有一个关键优化:不会每次都 new
    • 3. 函数类型 () -> Unit 底层是什么?
    • 4. 一张表彻底分清
    • 5. 最精炼总结(背这个)
  • FunctionN到底是函数类型 还是 函数式接口?
    • 1. 先看 FunctionN 到底是什么
    • 2. 函数类型是什么?
    • 3. 终极关系(必须背)
    • 4. 用一句最通俗的话总结
    • 5. 结论
  • 函数式接口和函数类型有什么区别?
    • 1. 先看最直观的对比
      • 1. 函数式接口(你写的)
      • 2. 函数类型(Kotlin 自带的)
    • 2. 5 个核心区别(背这个就够)
      • 1. 本质不同
      • 2. 来源不同
      • 3. 方法名不同
      • 4. 使用方式不同
      • 5. 用途不同
    • 3. 最关键的底层真相(超级重要)
    • 4. 那为什么还要分两个?
  • 函数类型 是不是 SAM?
  • 函数式接口 是不是 SAM?
    • lambda 是函数类型还是函数式接口?
  • 用 fun interface 这个定义的就是函数式接口?
    • 只要用 fun interface 定义的接口 = 一定是 函数式接口
    • 一定符合 SAM = 一定支持 SAM 转换(lambda 直接用)
    • 1. 最核心规则(背下来)
    • 2. 例子一看就懂
      • ✅ 正确:函数式接口
      • ❌ 错误:编译器直接报错
    • 3. 和普通接口的区别
    • 4. 最终结论(最精简)
  • SAM 是一个抽象函数 那两个以上的抽象函数叫什么?
    • 简单对应关系
    • 举个例子
      • 1 个抽象方法 → SAM
      • 2 个抽象方法 → 普通接口(没有特殊名字)
    • 超简总结
  • SAM 存在的意义 函数式接口的意义 函数类型的意义?
    • 1. SAM 的存在意义(最底层规则)
      • 详细解释
      • 核心
    • 2. 函数式接口 的存在意义(给SAM加约束+语义)
      • 1. 强制语法校验
      • 2. 赋予业务语义
      • 3. 支持默认方法、静态方法
    • 3. 函数类型 的存在意义(Kotlin纯函数式设计)
      • 存在目的
      • 区别直白版
    • 4. 三层层级关系 + 各自意义汇总
  • 函数类型有简化成lambda的形式么?
    • 对比一看就懂
      • ① 函数式接口(需要 SAM 转换)
      • ② 函数类型(直接就是 lambda,天然支持)
    • 高阶函数里最常见(你天天都在用)
    • 关键区别(一定要记住)
  • Java匿名内部类 → SAM → Lambda → 函数式接口 → 函数类型演进图?
    • 1. Java 匿名内部类
    • 2. 人们发现:很多接口只有一个抽象方法
    • 3. Java 8 利用 SAM 规则
    • 4. Kotlin 觉得还不够
    • 5. 最终结果
      • 1. SAM 规则
      • 2. 函数式接口
      • 3. FunctionN 接口
      • 4. 函数类型
      • 5. Lambda
  • lambda能赋值给函数式接口和SAM么?
    • 最直观例子
      • 1. 定义一个函数式接口(Kotlin)
      • 2. lambda 直接赋值给它 ✅
      • 3. 调用
    • 为什么可以?
    • 再给你一个 Java 接口的例子(更常见)
    • 反过来:函数类型不能赋值给随便一个接口

SAM

概念

1. SAM(Single Abstract Method)

所有只有一个抽象方法的接口,不管是谁写的、加没加注解,都是 SAM。

不是接口,不是类型,是一个规则:

一个接口里只有一个抽象方法

满足这个规则,就叫 SAM 类型。

// Java 里天然 SAM
public interface Runnable {
    void run(); // 只有这一个抽象方法
}

2. 函数式接口(Functional Interface)

就是满足 SAM 规则的接口

  • Java 里:加 @FunctionalInterface 标记
  • Kotlin 里:用 fun interface 声明

它们本质是接口,只是长得像函数。

// Kotlin 函数式接口 = SAM 接口
fun interface Calculator {
    fun calc(a: Int, b: Int): Int
}

3. 函数类型(Function Type)

Kotlin 原生的“函数形状”类型,不是接口,是语言内置类型:

  • () -> Unit
  • (Int) -> String
  • suspend () -> Result<Unit>

它是纯函数式的轻量类型,没有类、没有接口、没有方法。

极简记忆

  • SAM:接口规则(单一抽象方法)
  • 函数式接口:满足 SAM 的接口、被标记/命名为函数式的 SAM
  • 函数类型:Kotlin 内置的函数签名类型,不是接口
  • SAM 转换:Kotlin 把 lambda/函数类型 自动转成 函数式接口 的语法糖
  • 所有单方法接口都符合 SAM,都能用 lambda 简化

一句话记忆:函数类型是原生函数,函数式接口是 SAM 接口,SAM 转换是它们之间的桥。

  • 抽象方法数量 = 1 → 就是 SAM
  • 有默认方法 / 静态方法 / 静态内部类 → 不影响 SAM
  • 只要是 SAM,Kotlin 就可以用 lambda 直接 SAM 转换
  • 带默认方法、静态方法的函数式接口,100% 符合 SAM。

SAM、函数类型 和 函数式接口 是什么关系?

一句话总关系

  1. SAM = 一种接口规则:只有一个抽象方法的接口
  2. 函数式接口 = 满足 SAM 规则的接口
  3. 函数类型 = Kotlin 原生的“函数签名类型”
  4. SAM 转换 = Kotlin 允许把 lambda/函数类型 自动转成 函数式接口

它们是三层关系 + 一座桥梁。

三者真正的关系(核心图)

SAM 规则
    ↓
函数式接口(满足 SAM 的接口)
    ↘
     SAM 转换  ←—————— 桥梁
    ↗
函数类型 / lambda

关系拆解(非常关键)

1. 函数式接口 必须满足 SAM

  • 函数式接口 = SAM 接口
  • 不满足 SAM → 不能叫函数式接口

2. 函数类型 和 SAM 无关

函数类型是 Kotlin 内置类型,根本不是接口,所以和 SAM 没关系。

3. SAM 转换 = 把函数类型 / lambda → 自动变成 函数式接口

这就是 Kotlin 做的语法糖:

// 函数类型
val f: (Int, Int) -> Int = { a, b -> a + b }

// 函数式接口(SAM)
fun interface Calculator {
    fun calc(a: Int, b: Int): Int
}

// SAM 转换:函数类型 → 自动转成接口实例
val c: Calculator = f

等价于:

val c = Calculator { a, b -> a + b }

除了函数式接口符合sam 还有哪些符合SAM?

先说结论: 只要是「只有一个抽象方法的接口」,不管是 Java 还是 Kotlin 写的,全都符合 SAM。 不一定要叫“函数式接口”,也不一定要加任何注解。

到底什么才算 SAM?

满足这两条就是 SAM 类型:

  1. 是接口(不是类、不是抽象类)
  2. 里面只有一个抽象方法
    • 可以有默认方法、静态方法,不影响
    • 可以不加 @FunctionalInterface、不加 fun interface

所以:

  • Java 里的普通单方法接口 = SAM
  • Kotlin 普通单方法接口 = SAM(1.4+)
  • 加了 @FunctionalInterface 的 Java 接口 = SAM
  • 加了 fun interface 的 Kotlin 接口 = SAM

它们全都支持 SAM 转换。

常见符合 SAM 的接口(不全是“函数式接口”)

1. Java 标准库里大量普通接口

这些都不是特意标为函数式接口,但天然是 SAM:

  • Runnable
  • Callable<V>
  • ActionListener
  • Comparator<T>
  • FileFilter
  • FilenameFilter
  • InvocationHandler

示例:

// 普通 Java 接口,也是 SAM,可以直接 lambda
val r: Runnable = { println("hi") }

2. Android 里各种监听接口

全是 SAM,只是没人叫它们“函数式接口”:

  • View.OnClickListener
  • View.OnLongClickListener
  • TextWatcher(部分方法有默认实现后也算)
  • RecyclerView.OnScrollListener
button.setOnClickListener { } // 典型 SAM 转换

3. 你自己写的普通单方法接口

不加 fun interface、不加任何注解,照样是 SAM:

// 普通 Kotlin 接口,只有一个抽象方法 → SAM
interface MyCallback {
    fun onResult(data: String)
}

// 直接 lambda,自动 SAM 转换
val callback: MyCallback = { data -> println(data) }

注意: Kotlin 1.4+ 开始,普通单方法接口也支持 SAM 转换, 不再必须写 fun interface。

哪些符合【SAM 规则】?

所有 只有 1 个抽象方法 的接口 不管是 Kotlin / Java 不管有没有注解 不管叫不叫函数式接口 只要 = 1 个抽象方法 → 就是 SAM

哪些是【函数式接口】?

只有 2 种:

  1. Kotlin:用 fun interface 定义的接口
  2. Java:加了 @FunctionalInterface 注解的接口

除了 FunctionN,还有哪些是【函数式接口】?

1. Java 标准库自带(大量!)

@FunctionalInterface
public interface Runnable { void run(); }

@FunctionalInterface
public interface Consumer<T> { void accept(T t); }

@FunctionalInterface
public interface Predicate<T> { boolean test(T t); }

@FunctionalInterface
public interface Function<T,R> { R apply(T t); }

@FunctionalInterface
public interface Supplier<T> { T get(); }

@FunctionalInterface
public interface Comparator<T> { int compare(T a,T b); }

✅ 这些全部是函数式接口 ✅ 全部是 SAM ✅ 全部支持 lambda

2. Kotlin 你自己写的(fun interface)

fun interface MyClick { fun onClick() }
fun interface MyCallback { fun onResult(data: String) }

✅ 只要加 fun interface → 自动 = 函数式接口

3. Kotlin 内置 FunctionN(你已经知道)

Function0
Function1
Function2
...

✅ 也是函数式接口

除了函数式接口,还有哪些符合 SAM 规则?

超级重点:

SAM ≠ 函数式接口SAM 范围更大!

只要接口里 只有 1 个抽象方法,就算没加注解、没加 fun,依然是 SAM!

1. Java 普通单方法接口(无注解)

public interface MyTask { void execute(); }

✅ 是 SAM ❌ 不是函数式接口(没加 @FunctionalInterface)

2. Android 里大量监听(全是 SAM)

public interface OnClickListener { void onClick(View v); }
public interface OnLongClickListener { boolean onLongClick(View v); }

✅ 全是 SAM ❌ 都不是函数式接口

3. Kotlin 普通单方法接口(不加 fun)

interface MyAction { fun doAction() }

✅ 是 SAM(1个抽象方法) ❌ 不是函数式接口(没有 fun interface)

SAM和函数式接口最清晰分类(背这个就够)

【函数式接口】(小圈)

  • fun interface
  • @FunctionalInterface
  • FunctionN
  • Runnable / Consumer / Predicate / Function / Supplier

【SAM 接口】(大圈)

包含上面所有 + 下面这些:

  • 任何只有 1 个抽象方法的 Java 接口
  • 任何只有 1 个抽象方法的 Kotlin 普通接口
  • Android 各种 Listener
  • 你自己写的单方法接口

哪些不算 SAM?

  1. 有 两个或以上抽象方法 的接口
  2. 抽象类(哪怕只有一个抽象方法)
  3. 类、枚举、object
  4. Kotlin 函数类型 () -> Unit(它根本不是接口)

有默认方法、静态方法的函数式接口符合 SAM 的例子

SAM 只看:有且仅有一个抽象方法。 不管有多少默认方法、静态方法,都依然是 SAM,依然支持 SAM 转换。

1. Java 标准库自带的经典例子

① Comparator<T>(最经典)

抽象方法只有 1 个:compare(a, b) 但里面一堆默认方法、静态方法:

@FunctionalInterface
public interface Comparator<T> {

    // 唯一抽象方法 ← 满足 SAM
    int compare(T o1, T o2);

    // 默认方法(不影响 SAM)
    default Comparator<T> reversed() { ... }
    default Comparator<T> thenComparing(...) { ... }

    // 静态方法(也不影响)
    static <T extends Comparable<? super T>> Comparator<T> naturalOrder() { ... }
    static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) { ... }
}

在 Kotlin 里照样可以 SAM 转换:

val cmp: Comparator<Int> = { a, b -> a - b }

② Iterable 的迭代器相关不算,但看这个:Consumer<T>

@FunctionalInterface
public interface Consumer<T> {

    // 唯一抽象方法
    void accept(T t);

    // 默认方法
    default Consumer<T> andThen(Consumer<? super T> after) { ... }
}

依然是 SAM,依然能 lambda:

val c: Consumer<String> = { println(it) }

2. 你自己写一个:带默认 + 静态方法的 SAM 接口

Java 版本

@FunctionalInterface
public interface Worker {

    // 唯一抽象方法 → SAM 核心
    void doWork();

    // 默认方法
    default void rest() {
        System.out.println("休息一下");
    }

    // 静态方法
    static void start() {
        System.out.println("开始工作");
    }
}

在 Kotlin 中使用:

// SAM 转换成功
val worker: Worker = { println("干活") }

worker.doWork()  // 自己实现的
worker.rest()    // 默认方法
Worker.start()   // 静态方法

3. Kotlin 版本:fun interface + 默认方法

Kotlin 也完全支持:

fun interface Calculator {
    // 唯一抽象方法
    fun calculate(a: Int, b: Int): Int

    // 默认方法
    fun addOne(a: Int, b: Int): Int {
        return calculate(a, b) + 1
    }

    // 伴生对象里写静态方法
    companion object {
        fun create(): Calculator = Calculator { a, b -> a + b }
    }
}

使用:

// SAM 转换
val cal: Calculator = { a, b -> a * b }

cal.calculate(2,3)   // 6
cal.addOne(2,3)      // 7
Calculator.create()  // 静态方法

4. 再给一个 Android 里常见的

View.OnClickListener

public interface OnClickListener {
    // 唯一抽象方法
    void onClick(View v);

    // 未来可能加默认方法,依然是 SAM
}

所以你才能写:

btn.setOnClickListener { v -> ... }

函数式接口 底层是什么?

在 Kotlin / JVM 底层,函数式接口(SAM)用 lambda 写出来,本质还是匿名内部类,但做了优化,不是每次都 new 一个新对象。

1. 先看最直观的结论

你写的 SAM 转换 lambda:

val runnable: Runnable = { println("test") }

底层等价于:

val runnable = object : Runnable {
    override fun run() {
        println("test")
    }
}

所以: ✅ 函数式接口的 lambda,底层依然是匿名内部类实例 ✅ 它是一个真正的接口实现类对象 ✅ 不是函数类型,不是指针,不是特殊魔法

2. 但有一个关键优化:不会每次都 new

普通匿名内部类:

// 每次调用都会 new 一个新对象
button.setOnClickListener { ... }

SAM lambda 在 Kotlin 里会被编译器优化:

  • 如果 lambda 没有捕获外部变量(无闭包) → 编译器会生成单例,只创建一次,复用对象
  • 如果 lambda 捕获了变量 → 每次都会 new 一个新实例(和匿名内部类一样)

这是和手写 object : Runnable 最大的区别。

3. 函数类型 () -> Unit 底层是什么?

这个和 SAM 完全不一样,必须区分:

函数类型 () -> Unit 底层是:

Function0<Unit>

它是 JVM 上的一个真实类,Kotlin 内置的:

public interface Function0<out R> {
    R invoke();
}

所以:

  • 函数类型 = FunctionN 接口的实例
  • SAM 接口 = 你自己定义的接口的实例

但它们最终都是对象,只是接口不同。

4. 一张表彻底分清

写法底层是什么是否每次 new 对象?
手写匿名内部类
object : Runnable
匿名内部类每次都 new
SAM 转换 lambda
Runnable = { ... }
同样是 Runnable 实现类无捕获 → 单例(只创建1次)
有捕获 → 每次 new
函数类型 lambda
() -> Unit = { ... }
Function0 实例无捕获 → 单例(只创建1次)
有捕获 → 每次 new

5. 最精炼总结(背这个)

  1. 函数式接口(SAM)的 lambda,底层仍然是匿名内部类对象
  2. 它是接口的实现实例,不是函数,不是特殊类型
  3. 编译器会优化:不捕获变量时复用单例,捕获变量时才每次新建
  4. 函数类型 () -> Unit 底层是 FunctionN,和 SAM 不是同一个东西

一句话: SAM lambda 看着像函数,跑起来还是个接口对象。

如果你愿意,我可以给你看一段 反编译后的 Java 代码,让你亲眼看到它到底变成了什么类。

FunctionN到底是函数类型 还是 函数式接口?

  • FunctionN 是 Kotlin 内置的函数式接口(SAM 接口)
  • 函数类型 () -> R、(T) -> R 是 Kotlin 语法层面的写法
  • 编译到 JVM 字节码时,函数类型 会变成 FunctionN 接口

它们是语法层面 vs 字节码层面的关系,不是两个东西。


1. 先看 FunctionN 到底是什么

打开 Kotlin 标准库源码会看到:

// Function0.java
public interface Function0<out R> {
    R invoke();
}

// Function1.java
public interface Function1<in T, out R> {
    R invoke(T t);
}

很明显:

  • 是接口
  • 只有一个抽象方法 invoke()
  • 满足 SAM 规则
  • 是标准的函数式接口

所以: ✅ FunctionN 是函数式接口(SAM)

2. 函数类型是什么?

函数类型是 Kotlin 语言层面的语法糖:

  • () -> Unit → 对应 Function0<Unit>
  • (Int) -> String → 对应 Function1<Int, String>
  • (A, B) -> C → 对应 Function2<A, B, C>

它不是真实存在的 JVM 类型,编译后一律变成 FunctionN 接口。

3. 终极关系(必须背)

  1. FunctionN = JVM 字节码层面的函数式接口(SAM)
  2. 函数类型 (T) -> R = Kotlin 语法层面的别名
  3. lambda 表达式 → 编译后就是 FunctionN 的实现类

也就是说:

函数类型 在底层 就是 FunctionN 这个函数式接口。

4. 用一句最通俗的话总结

  • 你写:

    val f: (Int) -> String = { it.toString() }
    
  • 编译器心里想:

    Function1<Integer, String> f = ...;
    

所以:

  • FunctionN 是函数式接口
  • 函数类型是它的语法糖
  • 本质是同一个东西

5. 结论

FunctionN 到底是函数类型 还是 函数式接口?

标准答案:

FunctionN 是函数式接口(SAM 接口); 函数类型是 Kotlin 为 FunctionN 设计的语法表示。 二者本质等价,只是层级不同。

函数式接口和函数类型有什么区别?

函数式接口 = 你自己定义的接口(SAM)函数类型 = Kotlin 内置的通用函数形状(语法糖)

1. 先看最直观的对比

1. 函数式接口(你写的)

fun interface Click {
    fun onClick()
}
  • 是接口
  • 有名字:Click
  • 有方法名:onClick()
  • 是SAM
  • 必须通过 SAM 转换 变成 lambda

2. 函数类型(Kotlin 自带的)

() -> Unit
  • 是Kotlin 语法
  • 没有名字
  • 没有方法名,只有一个固定的 invoke()
  • 底层是 Function0 接口
  • 直接用 lambda,不需要转换

2. 5 个核心区别(背这个就够)

1. 本质不同

  • 函数式接口 = 接口(面向对象)
  • 函数类型 = 语言内置类型(函数式)

2. 来源不同

  • 函数式接口:你自己定义
  • 函数类型:Kotlin 天生自带

3. 方法名不同

  • 函数式接口:方法名随便起(onClick / call / run)
  • 函数类型:方法名固定叫 invoke()

4. 使用方式不同

  • 函数式接口:需要 SAM 转换
  • 函数类型:直接用 lambda

5. 用途不同

  • 函数式接口:用于 Java 互操作、自定义语义
  • 函数类型:用于 Kotlin 内部简洁传函数

3. 最关键的底层真相(超级重要)

函数类型 底层 = FunctionN 接口 = 也是函数式接口!

也就是说:

函数类型 本质上就是 Kotlin 官方给你预定义好的函数式接口

写:

() -> Unit

底层就是:

public interface Function0 {
    void invoke();
}

4. 那为什么还要分两个?

因为:

函数式接口 = 自定义、有名字、有业务含义

函数类型 = 通用、简洁、无名字、Kotlin 专属

例子:

  • Click → 表示点击(有含义)
  • () -> Unit → 表示“一个无参函数”(无含义)

函数类型 是不是 SAM?

不是。 SAM 是接口规则,函数类型是原生类型,完全不是一类东西。

函数式接口 是不是 SAM?

是。 函数式接口 = SAM 接口。

lambda 是函数类型还是函数式接口?

lambda 本身无类型,但:

  • 赋值给 () -> Unit → 变成函数类型
  • 赋值给 Runnable → 通过 SAM 转换 变成函数式接口实例

用 fun interface 这个定义的就是函数式接口?

对!100% 正确!

在 Kotlin 里:

只要用 fun interface 定义的接口 = 一定是 函数式接口

一定符合 SAM = 一定支持 SAM 转换(lambda 直接用)

1. 最核心规则(背下来)

fun interface 接口名 {
    // 必须有且只有一个抽象方法
}

满足这个写法 → 自动成为:

  1. SAM 接口(单抽象方法)
  2. 函数式接口
  3. 支持 lambda 直接赋值(SAM 转换)

2. 例子一看就懂

✅ 正确:函数式接口

fun interface MyAction {
    fun run() // 只有一个抽象方法
}

// 直接用 lambda ✅
val action: MyAction = { println("run") }

❌ 错误:编译器直接报错

fun interface MyAction {
    fun run()
    fun test() // 两个抽象方法 → 编译失败
}

fun interface 强制你只能有一个抽象方法 这就是它的作用。

3. 和普通接口的区别

写法是不是函数式接口能不能用 lambda
fun interface✅ 是✅ 可以
interface❌ 不是Kotlin 1.4+ 也可以,但不标准

4. 最终结论(最精简)

在 Kotlin 中:

fun interface = 函数式接口 = SAM 接口

一定可以用 lambda!

SAM 是一个抽象函数 那两个以上的抽象函数叫什么?

  • 只有 1 个抽象方法 → SAM 接口(Single Abstract Method)
  • ≥ 2 个抽象方法 → 就叫普通接口 / 多方法接口没有专门的英文缩写名字

简单对应关系

抽象方法数量叫什么支持 Kotlin SAM 转换(lambda)
1 个SAM 接口✅ 支持
2 个及以上普通接口❌ 不支持

举个例子

1 个抽象方法 → SAM

interface A {
    fun run() // 只有1个 → SAM
}

2 个抽象方法 → 普通接口(没有特殊名字)

interface B {
    fun run()
    fun cancel() // 2个 → 就是普通接口
}

这种接口不能用 lambda,只能老老实实写匿名内部类:

// 报错!不能 SAM 转换
val b: B = { ... }

// 只能这样写
val b = object : B {
    override fun run() {}
    override fun cancel() {}
}

超简总结

  • SAM = 单抽象方法接口
  • 多抽象方法接口 = 普通接口
  • 没有类似 SAM 这种专用简称

SAM 存在的意义 函数式接口的意义 函数类型的意义?

先前置定义,防止混淆:

  1. SAM:是规则 —— 接口仅有一个抽象方法(语法结构)
  2. 函数式接口:是被标记过的SAM —— Java:@FunctionalInterface,Kotlin:fun interface
  3. 函数类型:Kotlin原生语法 () -> Unit,底层是FunctionN

1. SAM 的存在意义(最底层规则)

一句话: SAM 就是为了让「接口」能用 Lambda。

详细解释

Java 早期没有Lambda,只能写匿名内部类; Java8 想加 Lambda,但不能改JVM,不能新增语法,只能复用现有接口。 发现:

只要接口只有一个抽象方法,编译器就能看懂你Lambda想实现哪个方法。

这就是 SAM 的初衷:

  1. 不用新增JVM类型,兼容老代码
  2. 单一抽象方法 → Lambda能唯一绑定这个方法
  3. 提供语法简化,干掉冗长匿名内部类
  4. Android各种点击监听、Runnable全部能简化

核心

SAM = Lambda能简写接口的前置条件。 没有SAM,就没有Java/Kotlin的Lambda简写接口。

2. 函数式接口 的存在意义(给SAM加约束+语义)

SAM是天生结构;函数式接口是人为规范好的SAM,安全、有语义、可扩展、兼容Java。

SAM只是客观结构(碰巧只有一个抽象方法); 函数式接口是人为定义、强制约束的SAM。

1. 强制语法校验

Java @FunctionalInterface、Kotlin fun interface:

  • 你写两个抽象方法 → 编译直接报错
  • 防止后期维护不小心多加方法,破坏SAM结构,Lambda全部失效

2. 赋予业务语义

fun interface OnClickListener { fun onClick(v:View) }

接口名有含义:点击事件。 对比裸的 (View)->Unit,可读性极强,适合Java调用、回调、Android接口设计。

3. 支持默认方法、静态方法

可以扩展能力,比如 Comparator 一堆默认方法,依然保持SAM。

3. 函数类型 的存在意义(Kotlin纯函数式设计)

一句话: 摆脱Java接口束缚,提供原生、极简、专门用来传递函数的类型。 写法:(Int,String)->Boolean

存在目的

  1. 不需要定义接口,直接描述函数签名,极简
  2. Kotlin高阶函数底层依赖它,集合map/filter全部靠它
  3. 专属于Kotlin,天然支持suspend挂起函数(Java做不到)
  4. 不用SAM转换,原生就是函数,写法最简单

底层:编译成FunctionN(也是SAM接口)

函数类型 = Kotlin官方预制好的一批函数式接口,语法简化给你用。

区别直白版

  • 函数式接口:有名字、有自定义方法、给Java交互用
  • 函数类型:无名字、固定invoke、纯Kotlin内部用

4. 三层层级关系 + 各自意义汇总

  1. SAM 意义:规则。保证Lambda可以映射到接口唯一抽象方法,Lambda语法可行的基础。

  2. 函数式接口 意义:规范后的SAM。编译校验、业务语义、可扩展默认方法、负责Kotlin与Java互操作。

  3. 函数类型 意义:Kotlin原生函数类型。极简、不用定义接口、高阶函数核心、纯Kotlin内部使用。

函数类型有简化成lambda的形式么?

有,而且函数类型本来就是为 lambda 服务的。 一句话: 函数类型 = 类型;lambda = 这个类型的具体值 / 写法。

它们天生就是一对,不需要 SAM 转换,直接就能用。

函数类型完全可以、也经常简化成 lambda 表达式。

比如:

  • 函数类型:() -> Unit

  • 对应 lambda:{ }

  • 函数类型:(Int) -> String

  • 对应 lambda:{ num -> num.toString() }

这是 Kotlin 最原生、最自然、最常用的写法, 不需要任何接口,不需要 SAM,直接写就行。

对比一看就懂

① 函数式接口(需要 SAM 转换)

fun interface Callback {
    fun call()
}

// 这里是 SAM 转换
val cb: Callback = { println("hi") }

② 函数类型(直接就是 lambda,天然支持)

// 函数类型  ← 直接赋值 lambda
val f: () -> Unit = { println("hi") }

没有任何区别,只是函数类型更简洁,不需要定义接口。

高阶函数里最常见(你天天都在用)

list.map { it * 2 }

这里:

  • map 接收的参数类型是 函数类型 (T) -> R
  • 你传的 { it * 2 } 就是 lambda

这就是函数类型简化为 lambda 的最典型例子。

关键区别(一定要记住)

  • 函数式接口用 lambda:需要走 SAM 转换
  • 函数类型用 lambda:直接就是它本身,不需要转换

函数类型不仅可以简化成 lambda, 而且 lambda 本来就是函数类型最标准、最简洁的写法。

  • 函数类型 = 杯子
  • lambda = 杯子里装的水

它们天生配套,不用任何中间层。

Java匿名内部类 → SAM → Lambda → 函数式接口 → 函数类型演进图?

1. Java 早期
   匿名内部类(又臭又长)
       ↓

2. Java 发现规律
   单方法接口 = SAM(只是结构,没名字)
       ↓

3. Java 8
   给 SAM 起名字:@FunctionalInterface 函数式接口
   → 支持 Lambda 简写(SAM 转换诞生)
       ↓

4. Kotlin 出现
   不想每次都定义接口,太麻烦
   → 直接内置一套函数类型:() -> Unit、(T) -> R …
       ↓

5. 最终形态
   函数类型 = 自带 Lambda,不用接口、不用转换

1. Java 匿名内部类

button.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        // 巨长
    }
});

问题:太啰嗦。

2. 人们发现:很多接口只有一个抽象方法

→ 给这种结构起名:SAM(Single Abstract Method)

SAM 不是技术,是规律。

3. Java 8 利用 SAM 规则

推出:

  • @FunctionalInterface(函数式接口)
  • 允许用 Lambda 代替匿名内部类 → 这就是 SAM 转换
button.setOnClickListener(v -> { });

意义:终于能简写了。

4. Kotlin 觉得还不够

每次用函数都要定义接口太烦 → 直接内置函数类型: () -> Unit、(Int) -> String、(A,B) -> C

底层还是 FunctionN 这种函数式接口,但用户不用关心。

5. 最终结果

val f: () -> Unit = { }

不用接口、不用SAM转换、直接Lambda。

这就是现代 Kotlin 最简洁形态。

匿名内部类 → 发现 SAM 规则 → 诞生函数式接口 → 支持 Lambda → Kotlin 再简化 → 函数类型(自带 Lambda,终极形态)

如果你需要,我可以再给你画一张三者关系闭环图: SAM ↔ 函数式接口 ↔ 函数类型,彻底闭环不混淆。

1. SAM 规则

  • 只有一个抽象方法的接口
  • 是最底层基础
  • 决定了能不能用 Lambda

2. 函数式接口

  • 是遵守 SAM 规则的接口
  • Kotlin:fun interface
  • Java:@FunctionalInterface
  • 有名字、有语义、可扩展默认方法

3. FunctionN 接口

  • Function0 / Function1 …
  • 是 Kotlin 内置的函数式接口
  • 也是 SAM
  • 是函数类型的底层真身

4. 函数类型

  • () -> Unit、(T) -> R
  • 是 Kotlin 给 FunctionN 做的语法糖
  • 最简洁、最易用
  • 不需要定义接口

5. Lambda

  • 是值 / 写法
  • 既能赋值给 函数类型
  • 也能通过 SAM 转换 赋值给 函数式接口

lambda能赋值给函数式接口和SAM么?

能!完全可以

而且这就是 SAM 转换 干的唯一一件事。

lambda 可以直接赋值给函数式接口, 前提是:函数式接口是 SAM(单抽象方法)。

lambda 能赋值给 → 所有 SAM 接口 SAM 接口 = 能接收 lambda 的接口

lambda 既能赋值给 SAM,也能赋值给函数式接口,也能赋值给函数类型。

最直观例子

1. 定义一个函数式接口(Kotlin)

fun interface Action {
    fun doIt()
}

2. lambda 直接赋值给它 ✅

val act: Action = { 
    println("我是 lambda,被赋值给了函数式接口") 
}

3. 调用

act.doIt()

这就叫 SAM 转换。

为什么可以?

因为:

  1. 函数式接口 只有一个抽象方法
  2. 编译器知道 lambda 就是在实现这个方法
  3. 自动帮你生成匿名内部类

再给你一个 Java 接口的例子(更常见)

val runnable: Runnable = { println("运行") }

Runnable 是 Java 的函数式接口, lambda 照样能直接赋值。

反过来:函数类型不能赋值给随便一个接口

val f: () -> Unit = { ... }

// 下面这句会报错!
val act: Action = f 

函数类型 不能直接赋值 给自定义函数式接口, 但 lambda 可以。

lambda → 可以直接赋值给 函数式接口(SAM 转换) 函数类型 → 不能直接赋值给 函数式接口