Lambda表达式全面综合笔记V1.0--后续会更新

Lambda表达式笔记

1. 为什么需要引入lambda?

痛点:传统方式的代码块传递问题。

因为java的面向对象特性,所以需要将代码块封装为一个类的方法然后在需要时进行传递。毕竟java参数当时还不能是代码块。

  1. 传统方式

    新建线程方式。

    @Test
    public void testNewThread(){
        new Thread(new A()).start(); //普通内部类
        new Thread(new Runnable() { //匿名内部类
                public void run() {
                    System.out.println("传统方式的代码块传递!");
                }
            }).start();
    }
    class A implements Runnable{
        public void run() {
            System.out.println("传统方式的代码块传递!");
        }
    }
    
  2. lambda方式

    new Thread(() -> System.out.println("lambda表达式方式!")).start();
    

​ 这样一行就解决了传递一个代码块的问题

2. lambda表达式的基本使用

2.1 函数式编程介绍

函数:一定的输入对应一定的输出

lambda表达式就是指一块代码,这块代码可以赋值给一个变量,或者作为方法参数

然后问题就是,这块代码赋值的变量类型是什么?

java8中,所有的lambda的类型都是一个接口,而Lambda表达式本身就是接口的实现。而且要求这个接口中只能有一个抽象方法(java8中还可以有默认方法),默认这块代码就是这个抽象方法的实现。这种接口称为函数式接口。

所以我们就可以自定义一个简单的例子了:

public interface A{
    void doSomething();
}

//测试方法中的代码
A a = () -> System.out.println("函数式接口的抽象方法");  

2.2 函数式接口

上述代码中的interface A就是一个函数式接口,也被称之为SAM接口,即Single Abstract Method Interfaces

特点:

  • 接口有且只有一个抽象方法
  • 允许定义静态方法
  • 允许定义默认方法
  • 允许java.lang.Object中的public方法(如equals等)
  • 该注解不是必需的,如果一个接口符合“函数式接口”的定义,那么加不加该注解都没有影响,比如上述接口A,加上注解是让编译器更好地进行检查,如果编写的不是函数式接口,也加了@FunctionalInterface,那么编译器会报错

例子

// 正确的函数式接口
@FunctionalInterface
public interface TestInterface {
 
    // 抽象方法
    public void sub();
 
    // java.lang.Object中的public方法
    public boolean equals(Object var1);
 
    // 默认方法
    public default void defaultMethod(){
    
    }
 
    // 静态方法
    public static void staticMethod(){
 
    }
}

// 错误的函数式接口(有多个抽象方法)
@FunctionalInterface
public interface TestInterface2 {

    void add();
    
    void sub();
}

JDK中的函数式接口举例

  1. java.lang.Runnable,

  2. java.awt.event.ActionListener,

  3. java.util.Comparator,

  4. java.util.concurrent.Callable

  5. java.util.function包下的接口,如Consumer、Predicate、Supplier等

    nametypedescription
    ConsumerConsuemr接收T对象,不返回值
    PredicatePredicate接收T对象并返回boolean
    FunctionFunction<T,R>接收T对象,返回R对象
    SupplierSupplier不接收值,返回T对象
    UnaryOperatorUnaryOperator接收T对象,返回T对象
    BinaryOperatorBinaryOperator接收两个T对象,返回T对象

    结合StreamAPI可以干很多事。

    Lambda可以理解为对应函数式接口的对象。

注意:

  1. lambda表达式中的this

    与传统的内部类不同,lambda不改变lambda的指向。

    @Test
    public void testLambdaThis(){
        System.out.println(this);
        new Thread( () -> System.out.println("this in lambda: " + this)).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("this in old way: "+ this);
            }
        }).start();
    }
    //lambda.TestLambda@1b701da1
    //this in lambda: lambda.TestLambda@1b701da1
    //this in old way: lambda.TestLambda$1@3c1971c
    
  2. Lambda表达式实现接口的六种类型,这里将两种返回值与三种参数类型进行组合举例

    @Test
    public void testLambdaInterface(){
        NoreturenNoParam noreturenNoParam = () -> System.out.println("NoreturenNoParam");
        noreturenNoParam.method();
        NoreturnOneParam noreturnOneParam = (a) -> System.out.println("NoreturnOneParam: " + a);
        noreturnOneParam.method(1);
        NoreturenMultiParam noreturenMultiParam = (a,b) -> System.out.println("NoreturenMultiParam: " + a +" "+ b);
        noreturenMultiParam.method(1,2);
        ReturnNoParam returnNoParam = () -> {
            System.out.println("ReturnNoParam");
            return 1;
        };
        returnNoParam.method();
        ReturnOneParam returnOneParam = (a) -> {
            System.out.println("ReturnOneParam: " + a);
            return 1;
        };
        returnOneParam.method(1);
        ReturnMultiParam returnMultiParam = (a,b) -> {
            System.out.println("ReturnMultiParam: " + a + " " + b);
            return 1;
        };
        returnMultiParam.method(1,2);
    }
    //NoreturenNoParam
    //NoreturnOneParam: 1
    //NoreturenMultiParam: 1 2
    //ReturnNoParam
    //ReturnOneParam: 1
    //ReturnMultiParam: 1 2
    
  3. 语句优化

    1. 简化参数类型,可以不写参数类型,因为实现的接口方法定义中有。
    2. 简化参数小括号,如果只有一个参数则可以省略小括号。
    3. 简化方法体大括号,如果方法只有一条语句,则可以省略大括号。
    4. 简化方法体大括号,如果方法只有一条return语句,则可以省略大括号。
    5. 如果这个方法与接口提供的参数和需要的返回值完全一致,就可以进一步简化为方法引用。

2.3 引用方法

快速指向一个已经被实现的方法。

语法

方法归属者::方法名

静态方法的归属者为类名,普通方法归属者为对象。

@Test
public void testLambdaRef(){
    Arrays.asList(1,2,3,4,5).forEach(System.out::println);
}

这里的调用的forEach函数如下:

需要出入一个Consumer接口,该接口的唯一抽象方法是:接收一个参数且没有返回值

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

而System.out.println也是接收一个参数且没有返回值。

public void println(Object x) {
    String s = String.valueOf(x);
    synchronized (this) {
        print(s);
        newLine();
    }
}

所以这里就可以直接将该方法作为Consumer的实现,用极简的方式实现方法引用(本质上也就是指向一个代码块),也可以称之为语法糖。

再举一些常用实例:

  1. removeIf方法

    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }
    

    这里需要一个Predicate接口,该接口的唯一抽象方法是:接受一个参数且返回值类型为boolean

    测试用例

     @Test
        public void testLambdaRef(){
            ArrayList<String> list = new ArrayList<>();
            list.add("damon1");
            list.add("damon2");
            list.add("damon3");
            list.add("evil");
            System.out.println(list.toString());
            list.removeIf(ele -> ele.contains("evil"));
            System.out.println(list.toString());
        }
    
    //[damon1, damon2, damon3, evil]
    //[damon1, damon2, damon3]
    
  2. sort方法

    public void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, size, c);
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }
    

    这里需要传入一个Comparator接口,该接口唯一的抽象方法是:接受两个参数且返回值为int

    测试用例

    @Test
    public void testLambdaRef() {
        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        class User {
            String name;
            int age;
        }
    
        ArrayList<User> users = new ArrayList<>();
        users.add(new User("damon1", 5));
        users.add(new User("damon1", 1));
        users.add(new User("damon1", 3));
        System.out.println(users.toString());
        users.sort((u1,u2) -> u1.getAge()-u2.getAge());
        System.out.println(users.toString());
    }
    
    //[User(name=damon1, age=5), User(name=damon1, age=1), User(name=damon1, age=3)]
    //[User(name=damon1, age=1), User(name=damon1, age=3), User(name=damon1, age=5)]
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值