Java 函数式接口

目标

我们的最终目的是理解这段代码:

Optional<String> tes = Optional.empty();
tes.ifPresent(System.out::println);

// 库实现
public void ifPresent(Consumer<? super T> action) {
    if (value != null) {
        action.accept(value);
    }
}

Consumer + Accept

只定义输入参数的类型,没有返回值类型定义,所以 accept 行为上也不返回值,只执行函数。

@FunctionalInterface
interface Consumer<T> {
    void accept(T t); // 无返回值,仅执行操作
}

Function + Apply

apply 就是向 Function<String,String> 的函数实例传递输入值,得到 transformer 的返回值。

import java.util.Optional;
import java.util.function.Function;

@FunctionalInterface
interface StringTransformer {
    String transFormer(String input);
}

class StringProcessor {
    String processor(String input, Function<String,String> tranformer) {
        return tranformer.apply(input);
    }
}

class toUpper {
    public static String toUpp(String input) {
        return input.toUpperCase();
    }
}

class Solution {
    public static void main(String[] args) {
        StringProcessor stringProcessor = new StringProcessor();
      
        String result = stringProcessor.processor("hello", toUpper::toUpp);
        System.out.println(result);
    }
}

函数式接口 VS Lambda VS 方法引用

Lambda 对象用来实现函数式接口,方法引用作为参数会被编译器展开包装成为 Lambda 对象。

PECS 原则

  • PECS = Producer Extends, Consumer Super
  • 生产者用 extends,消费者用 super
  • 如果你从集合里“生产”东西(拿出来,只读),用 ? extends T;(生产出的数据只能是当前类型或当前类型的子类)
  • 如果你往集合里“消费”东西(放进去,只写),用 ? super T。(子类有的其父类也有,消费安全)

为什么拿是生产,放是消费?
生产消费是相对于传入的容器来说的,容器生产东西你拿(生产者),或者是容器消费你放进去的东西(消费者)。

为什么子类只读,父类只写?
保证类型安全:类比权限系统,写入的对象类型必须包括容器类型中的所有类型的公共部分(最小权限原则),读取的类型必须包括容器中所有可能的类型(最大权限原则)。
比如说容器中是 List<? extends Number>,那其中可能是 Double, Integer 这些子类类型,

  • 写入时:写入 Double,但是实际存的是 Integer,出错;
  • 读取时:无论读出什么都是 Number 的子类,都可以当做 Number 来用。
    如果容器中是 List<? super String>,那其中可能是 String, Serializable, Object 这些父类类型,
  • 写入时:写入 Object,知道无论容器中是什么类型,写入 String 这个已定义类型一定是安全的;
  • 读取时:不能确定读取的确切类型。

配合方法引用

现在可以理解最初的目标,ifPresent 作为数据的消费者,希望接收一个 Consumer<? super T>。在初始化 Optional 对象的时候,就指定了泛型类型为 StringOptional<String> tes = Optional.of("test");,调用 ifPresent 的时候传入 System.out::println 这个方法引用,编译器处理这个语法糖,将其展开为 lambda 对象:ifPresent(s -> System.out.println(s)),这个 lambda 对象和 Consumer 这个函数式接口兼容(参数数量、参数类型、返回值类型能匹配上某个函数式接口的唯一抽象方法,lambda 就可以用来实现它)。然后通过非 null 检查后,通过 accept 调用方法,并传递打印方法的参数,最终输出结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yyt363045841

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值