Java8 Lambda 表达式的几重境界

在知乎上看到几个关于 Java 8 Lambda 表达式以及 stream 的文章,感觉写的不错,附上链接。

现代化 Java - Java8 指南
跟上Java8 - 了解lambda
Java 8 Stream 教程
乐字节|Java8核心特性实战-Lambda表达式
告别 For循环!用 Java Stream优雅处理集合
在这里插入图片描述
基础语法
在lambda中我们遵循如下的表达式来编写:

expression = (variable) -> action

variable: 这是一个变量,一个占位符。像x,y,z,可以是多个变量。
action: 这里我称它为action, 这是我们实现的代码逻辑部分,它可以是一行代码也可以是一个代码片段
lambda 表达式做的其实就是“传递方法”

用一句话来总结lambda表达式的作用,就是实现(new)一个接口,并且这个接口里只有一个方法。

先说第一层境界吧,来看lambda表达式该怎么用。

先举一个例子,创建线程的时候会用到Runnable接口,如下:

Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("do something.");      
    }
}

使用 lambda 表达式之后变为:

Runnable r = () -> System.out.println("do something.");

创建线程:

Thread t = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("do something");
    }
});
t.start();

lambda表达式为

new Thread(() -> System.out.println("do something")).start();

这里抛出一个问题,接口可以被new吗?面试会被 Q 到~~~~~~~
.
.
.
答案是可以的,但要使用匿名内部类,实现接口里面所有的方法。(但这个说法也不是很严谨,不能说是全对,详见接口和抽象类可以被new嘛?
单纯像new一个类一样new一个接口肯定是不行的。
我们来看一下Runnable接口的声明:

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

可用 lambda 表达式进行替换的条件总结为:

1、接口使用了@FunctionalInterface注解(非必须
2、接口里面只有一个抽象方法(必须
可以这样理解,因为接口里只有一个抽象方法,因此使用lambda表达式替换的方法就确定了,因此实现这个方法时方法名就可以不用管了,可以假设方法名对应为lambda表达式中的 ->,唯二可以视为“变量”的就是方法的返回值的类型(可以在{}里加return语句,当代码比较简单时return可以省略),方法入参(lambda表达式中可用简单变量名替代如x,y替代即(x, y, z));现在要new的接口中的抽象方法只剩下方法体了,可以直接用大括号{} + 方法体来替代,当方法体只有一行代码时{}也可以被省略了,所以最后我们常看到的lambda表达式就变成了

new Thread(() -> System.out.println("do something"))

总结:

lambda表达式使用的场景:

1、在方法A()的入参里有一个参数是接口,这个接口是用@FunctionalInterface注解修饰并且接口里只有一个抽象方法。
2、在具体调用这个方法A()时,需要将这个接口实例化,即使用匿名内部类的形式实现这个接口。
3、举报上面两个条件后就可以使用lambda表达式来实现这个方法,使用lambda表达式的步骤如下:
当使用 lambda 表达式对接口的实现进行替换时,如果接口没有声明变量可用 () 替代接口的方法签名,如果声明了变量则用简单变量名如x,y替代即(x, y, z)
-> 可理解为lambda表达式操作符;
-> 操作符右边为方法的实现,代码有多行时可用大括号 {} 包围,当代码比较简单时可省略大括号 {}。当接口中的抽象方法有返回值时,使用return 返回即可

() - > {    return "do something"}

有时也可以将return省略,比如:

(x, y) -> x + y;

这里先将lambda表达式的第一重境界,起码先读懂lambda表达式是干什么的,对于别人写的lambda表达式能认识ヾ(◍°∇°◍)ノ゙,还有就是自己能够写简单的lambda表达式。

第二重境界,来让我熟悉一下lambda表达式中常用的四个接口:

在这里插入图片描述

消费型接口示例:
public static void donation(Integer money, Consumer<Integer> consumer){
    consumer.accept(money);  
}
public static void main(String[] args) {
    donation(1000, money -> System.out.println("爱心捐赠" + money + "元")) ;
}

方法donation()中有一个入参Consumer<Integer> consumer是接口(条件一)
在主函数中调用方法donation()时要实例化接口(条件二)
查看Consumer接口里的accept()方法可知,accept()方法接收一个入参,因此lambda表达式 ->左边有一个入参money,右边即为accept()方法的具体实现,由于accept()方法返回值为void型,所以无返回值。
其中Consumer接口的声明为:

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

可以看出,lambda表达式的作用就是传递一个方法的实现。

供给型接口示例
public static List<Integer> supply(Integer num, Supplier<Integer> supplier){
       List<Integer> resultList = new ArrayList<>();
       for(int i = 0;i < num; i++)  
           resultList.add(supplier.get());
       return resultList ;
}
public static void main(String[] args) {
    List<Integer> list = supply(10, () -> (int)(Math.random()*100));
    list.forEach(System.out::println);
}
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

供给型接口没有入参,返回一个对象。上面代码的作用是随机生成10个数存入list并打印输出。

函数型接口示例

转换字符串为Integer

public static Integer convert(String str, Function<String, Integer> function) {
    return function.apply(str);
}
public static void main(String[] args) {
    Integer value = convert("28", x -> Integer.parseInt(x));
}
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
    
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

函数型接口中的apply()方法用于传入一个类型的参数,返回另外一个类型的参数,比如传入一个String,返回一个Integer,即将String转换为Integer

断言型接口示例

筛选出只有2个字的水果

public static List<String> filter(List<String> fruit, Predicate<String> predicate){
    List<String> f = new ArrayList<>();
    for (String s : fruit) {
        if(predicate.test(s)){
            f.add(s);
        }
    }
    return f;
}
public static void main(String[] args) {
    List<String> fruit = Arrays.asList("香蕉", "哈密瓜", "榴莲", "火龙果", "水蜜桃");
    List<String> newFruit = filter(fruit, (f) -> f.length() == 2);
    System.out.println(newFruit);
}
@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }

断言型接口里的test()方法有一个入参,返回一个boolean,常用于对入参进行一个条件的判断,返回判断结果。比如上面这个例子,使用lambda表达式简化的是Predicate接口里的test()方法,test方法传入一个String类型的变量,判断其长度是否为2,并返回判断的结果。

第三重境界,stream()lambda表达式结合使用

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值