Java Lambda表达式用法

185 阅读9分钟

未完待续。。。

概述

把函数作为参数传递进方法中,使用 Lambda 表达式可以使代码变的更加简洁紧凑。 lambda表达式的本质是匿名函数,比如以下函数:

public int add(int x, int y) {
    return x + y;
}

可以转换为

(int x, int y) -> x + y;

语法

lambda表达式的语法格式如下:

(parameter1, parameter2,...) -> expression
或
(parameter1, parameter2,...) ->{statements; }

使用注意

  • 参数类型不是必须(可以推导出),如:
    • Comparator<String> cmp = (first, second) -> first.length() - second.length();
  • 无参数时,()不可缺省,如:
    • () -> {for (int i = 0; i < 10; i++) System.out.print(i);}
  • 参数圆括号不是必须:一个参数无需定义圆括号;多个参数需要定义圆括号
  • 大括号不是必须:如果主体仅有一个语句,就不需要使用大括号
  • 返回关键字不是必须:如果主体只有一个表达式返回值,编译器会自动返回。大括号需要指定表达式返回了一个数值。
  • 不能只在某一个分支返回一个值,如:
    • (int x)->{if(x>=0)return 1;}

简单例子

// 不需要参数,返回值为5
() -> 5;

使用场景

Lambda表达式的应用非常广泛,它可以用于简化代码、实现函数式编程、处理集合、处理事件、并发编程等多个方面。根据不同的场景和需求,可以选择合适的函数式接口和Lambda表达式来实现相应的功能。

匿名内部

后面要用到,这里简单解释一下。

在方法中,如果需要使用一个未创建的类的实例对象,则可以直接通过new 父类,并重写父类中方法的形式,创建一个匿名类的实例对象,这样可以使得代码更简洁。

匿名类通常是继承父类或者实现一个接口。下图是语法格式:

image.png (图来自:菜鸟教程:Java匿名类

替代匿名内部类(实现函数式编程)

lambda表达式通常是结合函数式接口(有且只有一个抽象方法,但可以有多个非抽象方法的接口)一起使用。

如果某个方法A的传参是函数式接口类型,则可使用lamda表达式重写函数式接口中抽象方法的具体逻辑,直接传入方法A。详情见下面的例子。

Runnable接口

Thread类的构造方法,传入的参数类型是Runnable接口类型,则可在直接将lambda表达式传入new Thread方法中。

public class RunnableLambda {
    // 结合Runnable使用匿名内部类
    public void runnableAnonymous() {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("the runnable now is using!");
            }
        });
        thread.start();
    }

    // 结合Runnable使用匿名内部类2
    public void runnableAnonymous2() {
        //先创建好匿名内部类的实例对象,再将对象传入new Thread()方法
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("the runnable now is using!");
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
    }

    // 结合Runnable使用lambda表达式,直接将run方法的代码逻辑写出来传进去即可。
    // 因为new Runnable()构造方法不需要传入参数,所以lambda格式使用() -> {statement}
    public void runnableLambda(){
        Thread thread = new Thread(() -> System.out.println("it's a lambda function"));
        thread.start();
    }
}

Comparator接口

是一个函数式接口,用于定义对象的比较规则

public class ComparatorLambda {
    // 使用匿名内部类
    public void comparatorLambda(){
        List<Integer> strings = Arrays.asList(1, 2, 3);
        Collections.sort(strings, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
    }

    //使用lambda表达式
    public void comparatorLambda2(){
        List<Integer> strings = Arrays.asList(1, 2, 3);
        Collections.sort(strings, (Integer o1, Integer o2) -> o1 - o2);
        // 或者如下形式
        strings.sort((o1, o2) -> o1 - o2);
    }

    // 使用外部类
    public void comparatorLambda3() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        Collections.sort(list, new Comparator2());
    }


}

class Comparator2 implements Comparator<Integer>{

    @Override
    public int compare(Integer o1, Integer o2) {
        return o1 - o2;
    }
}

实际使用场景可以参考算法题(见提交记录):BM89合并区间

Predicate接口

用于表示一个断言(条件),通常用于过滤集合中的元素。

以下示例,使用Lambda表达式作为filter方法的参数(filter方法的传入参数是Predicate<? super T> predicate,可以看下源码),筛选出以“A”开头的姓名。

public class PredicateLambda {
    //使用匿名内部类
    public List<String> predicateAnonymous() {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
        List<String> result = names.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.startsWith("A");
            }
        }).collect(Collectors.toList());

        return result;
    }

    //使用lambda表达式
    public List<String> predicateLambda() {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
        List<String> result = names.stream().filter(name -> name.startsWith("A")).collect(Collectors.toList());
        return result;
    }

    public static void main(String[] args) {
        List<String> res =  new PredicateLambda().predicateAnonymous();
        res.forEach(name -> System.out.println(name));
    }

}

Consumer接口

java.util.function.Consumer是一个函数式接口,用于表示接受一个输入并没有返回值的操作。通常用于遍历集合,并对每个元素执行某种操作。

public class ConsumerLambda {
    //使用匿名内部类
    public void consumerAnonymous() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        numbers.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer number) {
                System.out.println(number);
            }
        });
    }

    //使用lambda表达式
    public void consumerLambda() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        numbers.forEach(number -> System.out.println("Number: " + number));
    }

    public static void main(String[] args) {
        new ConsumerLambda().consumerAnonymous();
        new ConsumerLambda().consumerLambda();
    }
    
}

Function接口

用于表示接受一个输入,并产生一个输出的函数

以下示例为:使用Lambda表达式作为map方法的参数,将整数列表中的每个元素平方,并将结果收集到新的列表中。

public class FunctionLambda {
    //使用匿名内部类
    public List<Integer> functionAnonymous() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        List<Integer> squaredNumbers = (List<Integer>) numbers.stream().map(new Function<Integer, Integer>() {
            @Override
            public Integer apply(Integer number) {
                return number * number;
            }
        }).collect(Collectors.toList());
        return squaredNumbers;
    }

    //使用lambda表达式
    public List<Integer> functionLambda() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        // map方法的传参是Function类型
        List<Integer> squaredNumbers = numbers.stream().map(number -> number * number).collect(Collectors.toList());
        return squaredNumbers;
    }

    // 调用
    public static void main(String[] args) {
        List<Integer> res = new FunctionLambda().functionAnonymous();
        res.forEach(num -> System.out.println(num));
    }
}

Supplier接口

用于表示一个供应商,通常用于延迟计算或提供默认值。

简单示例

public class SupplierLambda {
    //使用匿名内部类
    public int supplierAnonymous() {
        Supplier<Integer> randomSupplier = new Supplier<Integer>() {
            @Override
            public Integer get() {
                return new Random().nextInt(100);
            }
        };
        return randomSupplier.get();
    }

    // 使用lambda表达式
    public int supplierLambda() {
        Supplier<Integer> randomSupplier = () -> new Random().nextInt(100);
        int randomNumber = randomSupplier.get();
        return randomNumber;
    }

    public static void main(String[] args) {
        int res = new SupplierLambda().supplierAnonymous();
        System.out.println(res);
    }
}

某个方法的传参是Supplier接口类型

CompletableFuture.supplyAsync方法的传参是Supplier接口类型

public class CompletableFutureLambda {
    // 先创建好匿名内部类的实例对象,supplier,再传入CompletableFuture.supplyAsync方法
    public void supplierAnonymous() throws InterruptedException, TimeoutException, ExecutionException {
        UserInfoService userInfoService = new UserInfoService();
        long userId = 666L;

        Supplier supplier = new Supplier() {
            @Override
            public Object get() {
                return userInfoService.getUserInfo(userId);
            }
        };
        CompletableFuture<UserInfo> userInfoCompletableFuture = CompletableFuture.supplyAsync(supplier);
        UserInfo userInfo = userInfoCompletableFuture.get(2, TimeUnit.SECONDS);
        System.out.println(userInfo.getAge());
    }

    // 直接在CompletableFuture.supplyAsync方法中new匿名内部类的实例对象
    public void supplierAnonymous2() throws InterruptedException, TimeoutException, ExecutionException {
        UserInfoService userInfoService = new UserInfoService();
        long userId = 666L;

        CompletableFuture<UserInfo> userInfoCompletableFuture = CompletableFuture.supplyAsync(new Supplier<UserInfo>() {
            @Override
            public UserInfo get() {
                return userInfoService.getUserInfo(userId);
            }
        });

        UserInfo userInfo = userInfoCompletableFuture.get(2, TimeUnit.SECONDS);
        System.out.println(userInfo.getAge());

    }

    public void supplierLambda() throws InterruptedException, TimeoutException, ExecutionException {
        UserInfoService userInfoService = new UserInfoService();
        long userId = 666L;
        CompletableFuture<UserInfo> userInfoCompletableFuture =
                CompletableFuture.supplyAsync(() -> userInfoService.getUserInfo(userId));
        UserInfo userInfo = userInfoCompletableFuture.get(2, TimeUnit.SECONDS);
        System.out.println(userInfo.getAge());
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
        CompletableFutureLambda completableFutureLambda = new CompletableFutureLambda();
        completableFutureLambda.supplierLambda();
    }
}

Listener接口

public class ListenerLambda {
    //使用匿名内部类
    public void listenerAnonymous() {
        JButton button = new JButton();
        button.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                e.getItem();
            }
        });
    }
    
    //使用lambda表达式
    public void listenerLambda() {
        JButton button = new JButton();
        button.addItemListener(e -> e.getItem());
    }
}

自定义函数式接口

// 接口定义
@FunctionalInterface
public interface IntBinaryOperator {
    int apply(int a, int b);
}

// 使用
public class IntBinaryOperatorLambda {
    public int binaryOperatorLambda() {
        IntBinaryOperator add = (a, b) -> a + b;
        int result = add.apply(3, 5);
        return result;
    }

    public static void main(String[] args) {
        int res = new IntBinaryOperatorLambda().binaryOperatorLambda();
        System.out.println(res);
    }
}

集合迭代

public class CollectionLambda {
    //使用匿名内部类
    public void collectionAnonymous() {
        List<String> list = Arrays.asList("123","456");
        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
    }

    //使用lambda表达式
    public void collectionLambda() {
        ArrayList<String> list = new ArrayList<>(Arrays.asList("i", "live"));
        list.forEach(s -> System.out.println(s));
    }
}

方法引用

诸如String::length的语法形式叫做方法引用(method references),这种语法用来替代某些特定形式Lambda表达式。如果Lambda表达式的全部内容就是调用一个已有的方法,那么可以用方法引用来替代Lambda表达式。方法引用可以细分为四类:

方法引用类别举例
引用静态方法Integer::sum
引用某个对象的方法list::add
引用某个类的方法String::length
引用构造方法HashMap::new

方法引用调用的返回值必须赋值给一个函数式接口。\color{red}{方法引用调用的返回值必须赋值给一个函数式接口。}

实例1:

// 函数式接口
@FunctionalInterface
public interface LambdaInterface {
    void f();
}

// 父类
public class ReferenceLambdaSuper {
    LambdaInterface sf() {
        return null;
    }
}

//子类
public class ReferenceLambda extends ReferenceLambdaSuper {
    public static LambdaInterface staticF(){
        return null;
    }

    public LambdaInterface f() {
        return null;
    }

    void show() {
        //调用静态方法,返回值必须是函数式接口?(不一定吧)
        LambdaInterface f = ReferenceLambda::staticF;

        //实例方法调用
        ReferenceLambda referenceLambda = new ReferenceLambda();
        LambdaInterface lambdaInterface = referenceLambda::f;

        //调用父类方法
        LambdaInterface superf = super::sf;

        //构造方法调用
        LambdaInterface tt = ReferenceLambdaSuper::new;
    }
}

实例2:

public class User {
    private int age;

    public int getAge(){
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public static void main(String[] args) {
        Function<User, Integer> userIntegerFunction = o -> o.getAge();

        Function<User, Integer> userIntegerFunction1 = User::getAge;

        // 报错:int is not a functional interface
        int a = User::getAge;
    }
}

实例3:

/**
 * @author lx
 */
public class Person {
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


    public static void print() {
        System.out.println("静态方法");
    }

    public static Person instance() {
        return new Person();
    }


    public Integer gett(Object str, Integer integer) {
        setAge(integer);
        return getAge();
    }


    public Integer gett(int integer, Object str) {
        setAge(integer);
        return getAge();
    }

    @Test
    public void test() {
        //一个user实例
        Person user = new Person();

        //类型上的实例方法引用:ClassName::methodName
        Function<Person, Integer> userIntegerFunction2 = Person::getAge;

        //类型上的实例方法引用:ClassName::methodName
        Consumer<Person> userConsumer2 = System.out::println;



        //实例上的实例方法引用:instanceReference::methodName
        Supplier<Integer> supplier = user::getAge;


        //类型上的静态方法引用:ClassName::methodName
        Supplier<Person> userSupplier = Person::instance;

        //类型上的静态方法引用:ClassName::methodName
        Print print = Person::print;



        //超类实例上的实例方法引用:super::methodName
        Supplier<Class> SupSupplier = super::getClass;




        //构造方法引用:ClassName::new
        Supplier<Object> newSupplier = Person::new;




        //数组构造方法引用:TypeClassName[]::new
        Function<Integer, Person[]> arrayFunction = Person[]::new;



        
        //如果内部调用gett(String str, Object integer)方法
        ThFunction<Person, Integer, Integer, String> thFunction1 = new ThFunction<Person, Integer, Integer, String>() {
            @Override
            public Integer apply(Person o, Integer o2, String o3) {
                return o.gett(o3, o2);
            }
        };
        //那么不能使用方法引用
        //因为虽然抽象方法的第一个参数就是内部调用该方法的实例,后面的参数和方法参数的顺序不一致
        //参数顺序不一致,即  o2 o3  ->  o3 o2
        ThFunction<Person, Integer, Integer, String> thFunction2 = (o, o2, o3) -> o.gett(o3, o2);


        //如果内部调用gett(Integer integer, Object str)方法
        ThFunction<Person, Integer, Integer, String> thFunction3 = new ThFunction<Person, Integer, Integer, String>() {
            @Override
            public Integer apply(Person o, Integer o2, String o3) {
                return o.gett(o2, o3);
            }
        };
        //那么能使用方法引用
        //因为抽象方法的第一个参数就是内部调用该方法的实例,后面的参数和方法参数的顺序一致,类型兼容
        //参数顺序一致,即  o2 o3  ->   o2 o3
        ThFunction<Person, Integer, Integer, String> thFunction4 = Person::gett;
    }


    /**
     * 自定义函数式接口,无参数无返回值
     */
    @FunctionalInterface
    public interface Print {
        /**
         * 输出
         */
        void print();
    }

    @FunctionalInterface
    public interface ThFunction<T, U, R, K> {
        R apply(T t, U u, K k);
    }
}

  1. CSDN blog:秒懂Java之方法引用(method reference)详解
  2. Lambda表达式、方法引用、默认方法的详细介绍与使用案例

变量作用域

处理lambda表达式


参考文章:

  1. Java-lambda表达式入门看这一篇就够了

  2. 【Java 基础篇】Java Lambda表达式详解

  3. 关于 Java Lambda 表达式看这一篇就够了

  4. JavaGuide:Java8新特性