JDK8新特性——Lambda表达式

    科技2025-10-18  10

    Lambda表达式不是Java最早使用的,很多语言就支持Lambda表达式,例如:C++,C#,Python,Scala等。如果有Python或者Javascript的语言基础,对理解Lambda表达式有很大帮助,可以这么说lambda表达式其实就是实现SAM接口的语法糖,使得Java也算是支持函数式编程的语言。Lambda写的好可以极大的减少代码冗余,同时可读性也好过冗长的匿名内部类。

    1、Lambda表达式引入

    示例1:Runnable实现线程

    public void test1(){ new Thread(new Runnable(){ public void run(){ System.out.println("do something.."); } }).start(); } public void test2(){ new Thread(() -> System.out.println("do something..")).start(); }

    示例2:foreach遍历

    public void test3(){ List<String> list = Arrays.asList("hello","java","bdit","lambda"); for (String string : list) { System.out.println(string); } } public void test4(){ List<String> list = Arrays.asList("hello","java","bdit","lambda"); list.forEach(System.out::println); }

    示例3:FileFilter文件过滤

    public void test5(){ //1.文件目录 File fileDir=new File("D:/resource"); //2.创建筛选规则,帅选出所有.java文件 FileFilter filter=new FileFilter() { @Override public boolean accept(File file) { if(!file.isDirectory()&&file.getName().endsWith(".java")){ return true; } return false; } }; //3.得到筛选文件 File[] files=fileDir.listFiles(filter); for (File file : files) { System.out.println(file); } } public void test6(){ //1.文件目录 File fileDir=new File("D:/resource"); //2.得到筛选文件,帅选出所有.java文件 File[] files=fileDir.listFiles((file) -> !file.isDirectory() && file.getName().endsWith(".java")); //3、遍历查看结果 Arrays.asList(files).forEach(System.out::println); }

    2、函数式接口概念

    Lambda表达式是用来实现SAM接口的,所谓SAM接口就是Single Abstract Method,即该接口中只有一个抽象方法需要实现,当然该接口可以包含其他非抽象方法。

    其实只要满足“SAM”特征的接口都可以称为函数式接口,但是如果要更明确一点,最好在声明接口时,加上@FunctionalInterface。

    JDK1.8之前,核心类库中就已经存在很多SAM接口了,例如: (1)java.lang.Runnable (2)java.util.concurrent.Callable (3)java.util.Comparator (4)java.lang.Comparable (5)java.lang.Iterable (6)java.io.FileFilter (7)java.lang.reflect.InvocationHandler …等

    但是在JDK1.8,只有(1)(2)(3)(6)加了@FunctionalInterface,那些没有加@FunctionalInterface的SAM接口,现在使用Lambda表达式实现,但是存在将来增加抽象方法变成非SAM接口的风险,因此建议只对加了@FunctionalInterface的接口使用Lambda表达式实现。

    JDK1.8在java.util.function包增加了很多函数式接口,不过他们可以归纳为四类:消费型接口、供给型接口、功能型接口、判断型接口,一共43个,基本上可以满足开发中函数式接口的基本使用需求,如你在开发中需要设计函数式接口,请先从以下接口中选择是否有满足需求的,如果有几不需要重新设计了。

    1、消费性接口

    这类接口的抽象方法特点:有形参,但是返回值类型是void

    接口名抽象方法描述Consumer< T>void accept(T t)接收一个对象用于完成功能BiConsumer<T,U>void accept(T t, U u)接收一个对象用于完成功能DoubleConsumervoid accept(double value)接收一个double值IntConsumervoid accept(int value)接收一个int值LongConsumervoid accept(long value)接收一个long值ObjDoubleConsumer< T>void accept(T t, double value)接收一个对象和一个double值ObjIntConsumer< T>void accept(T t, int value)接收一个对象和一个int值ObjLongConsumer< T>void accept(T t, long value)接收一个对象和一个long值

    2、供给型接口

    这类接口的抽象方法特点:无参,但是有返回值

    接口名抽象方法描述Supplier< T>T get()返回一个对象BooleanSupplierboolean getAsBoolean()返回一个boolean值DoubleSupplierdouble getAsDouble()返回一个double值IntSupplierint getAsInt()返回一个int值LongSupplierlong getAsLong()返回一个long值

    3、判断型接口

    这里接口的抽象方法特点:有参,但是返回值类型是boolean结果。

    接口名抽象方法描述Predicate< T>boolean test(T t)接收一个对象BiPredicate<T,U>boolean test(T t, U u)接收两个对象DoublePredicateboolean test(double value)接收一个double值IntPredicateboolean test(int value)接收一个int值LongPredicateboolean test(long value)接收一个long值

    4、功能型接口

    这类接口的抽象方法特点:既有参数又有返回值

    接口名抽象方法描述Function<T,R>R apply(T t)接收一个T类型对象,返回一个R类型对象结果UnaryOperator< T>T apply(T t)接收一个T类型对象,返回一个T类型对象结果DoubleFunction< R>R apply(double value)接收一个double值,返回一个R类型对象IntFunction< R>R apply(int value)接收一个int值,返回一个R类型对象LongFunction< R>R apply(long value)接收一个long值,返回一个R类型对象ToDoubleFunction< T>double applyAsDouble(T value)接收一个T类型对象,返回一个doubleToIntFunction< T>int applyAsInt(T value)接收一个T类型对象,返回一个intToLongFunction< T>long applyAsLong(T value)接收一个T类型对象,返回一个longDoubleToIntFunctionint applyAsInt(double value)接收一个double值,返回一个int结果DoubleToLongFunctionlong applyAsLong(double value)接收一个double值,返回一个long结果IntToDoubleFunctiondouble applyAsDouble(int value)接收一个int值,返回一个double结果IntToLongFunctionlong applyAsLong(int value)接收一个int值,返回一个long结果LongToDoubleFunctiondouble applyAsDouble(long value)接收一个long值,返回一个double结果LongToIntFunctionint applyAsInt(long value)接收一个long值,返回一个int结果DoubleUnaryOperatordouble applyAsDouble(double operand)接收一个double值,返回一个doubleIntUnaryOperatorint applyAsInt(int operand)接收一个int值,返回一个int结果LongUnaryOperatorlong applyAsLong(long operand)接收一个long值,返回一个long结果BiFunction<T,U,R>R apply(T t, U u)接收一个T类型和一个U类型对象,返回一个R类型对象结果BinaryOperator< T>T apply(T t, T u)接收两个T类型对象,返回一个T类型对象结果ToDoubleBiFunction<T,U>double applyAsDouble(T t, U u)接收一个T类型和一个U类型对象,返回一个doubleToIntBiFunction<T,U>int applyAsInt(T t, U u)接收一个T类型和一个U类型对象,返回一个intToLongBiFunction<T,U>long applyAsLong(T t, U u)接收一个T类型和一个U类型对象,返回一个longDoubleBinaryOperatordouble applyAsDouble(double left, double right)接收两个double值,返回一个double结果IntBinaryOperatorint applyAsInt(int left, int right)接收两个int值,返回一个int结果LongBinaryOperatorlong applyAsLong(long left, long right)接收两个long值,返回一个long结果

    3、Lambda表达式

    Lambda表达式是用来实现SAM接口的,它相当于一个匿名函数,语法格式如下:

    (Type1 param1, Type2 param2, ..., TypeN paramN) -> { statment1; statment2; //............. return statmentM; }

    简单的说就是:

    (形参列表) -> {Lambda体}

    这个操作符为 “->” , 该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分: (1)左侧:指定了 Lambda 表达式需要的参数列表,它其实就是函数式接口的抽象方法的形参列表 (2)右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能,它其实就是实现函数式接口的抽象方法的方法体。

    例如:Lambda表达式只写了Runnable接口的抽象方法public void run()的()空参列表以及对run()方法的实现代码。

    public void test1(){ new Thread(new Runnable(){ public void run(){ System.out.println("do something.."); } }).start(); } public void test2(){ new Thread(() -> System.out.println("do something..")).start(); }

    格式要求: 1、关于(形参列表) (1)如果没有形参,那么()不可以省略; (2)如果有形参,并且形参只有一个,并且形参类型已知或可推断,那么可以省略()和数据类型,只写形参名; (3)如果形参不止一个,那么()不可以省略,但是如果形参类型已知或可推断,那么可以数据类型。

    2、关系{Lambda体} (1)如果函数式接口的抽象方法有返回值,即返回值类型不是void,那么Lambda体必须要有“return 返回值;”语句; (2)如果{Lambda体}只有一个语句,那么{}可以省略,如果{}省略了,那么语句后面的;也要省略,如果{Lambda体}只有一个return 返回值;语句,那么{return;}都可以省略,Lambda体只写返回值即可。

    形式一:无参无返回值

    代码示例:Runnable函数式接口

    package com.bdit.lambda.learn; import org.junit.Test; public class TestLambda1 { public void test1() { //不使用Lambda表达式 new Thread(new Runnable() { public void run() { System.out.println("do something.."); } }).start(); } public void test2() { //使用Lambda表达式 new Thread(() -> { System.out.println("do something.."); }).start(); } public void test3() { //省略了可以省略的部分 new Thread(() -> System.out.println("do something..")).start(); } }

    形式二:有参无返回值

    代码示例:Consumer接口

    在JDK1.8中Collection集合接口的父接口Iterable接口中增加了一个默认方法:default void forEach(Consumer<? super T> action) ,遍历Collection集合的每个元素,执行“xxx消费型”操作。

    在JDK1.8中Map集合接口中增加了一个默认方法:default void forEach(BiConsumer<? super K,? super V> action)遍历Map集合的每对映射关系,执行“xxx消费型”操作。

    package com.bdit.lambda.learn; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map.Entry; import java.util.Set; public class TestLambda2 { public void test1(){ //不使用Lambda表达式 List<String> list = Arrays.asList("hello","java","lambda","bdit"); for (String string : list) { System.out.println(string); } HashMap<Integer,String> map = new HashMap<>(); map.put(1, "hello"); map.put(2, "java"); map.put(3, "lambda"); map.put(4, "bdit"); Set<Entry<Integer, String>> entrySet = map.entrySet(); for (Entry<Integer, String> entry : entrySet) { System.out.println(entry.getKey() + "->" + entry.getValue()); } } public void test2(){ //使用Lambda表达式 List<String> list = Arrays.asList("hello","java","lambda","bdit"); list.forEach((String s) -> {System.out.println(s);}); HashMap<Integer,String> map = new HashMap<>(); map.put(1, "hello"); map.put(2, "java"); map.put(3, "lambda"); map.put(4, "bdit"); map.forEach((Integer k,String v) -> {System.out.println(k+"->"+v);}); } public void test3(){ //省略了可以省略的部分 List<String> list = Arrays.asList("hello","java","lambda","bdit"); list.forEach(s -> System.out.println(s)); HashMap<Integer,String> map = new HashMap<>(); map.put(1, "hello"); map.put(2, "java"); map.put(3, "lambda"); map.put(4, "bdit"); map.forEach((k,v) -> System.out.println(k+"->"+v)); } }

    形式三:无参无返回值

    代码示例:Supplier接口

    在JDK1.8中增加了StreamAPI,Stream是一个数据流,一个不同于集合的数据流。关于Stream的详细介绍请看我博客Stream API。

    package com.bdit.lambda.learn; import java.util.stream.Stream; public class TestLambda3 { public void test1(){ //使用Lambda表达式 Stream<Double> stream = Stream.generate(() -> {return Math.random();}); stream.forEach((Double num) -> {System.out.println(num);}); } public void test2(){ //省略了可以省略的部分 Stream<Double> stream = Stream.generate(() -> Math.random()); stream.forEach(num -> System.out.println(num)); } }

    形式四:有参有返回值

    代码示例:Funtion<T,R>接口

    在JDK1.8时Map接口增加了很多方法,其中一个是:default void replaceAll(BiFunction<? super K,? super V,? extends V> function) 按照function指定的操作替换map中的value。

    package com.bdit.lambda.learn; import java.util.HashMap; public class TestLambda4 { public void test1(){ HashMap<String,Employee> map = new HashMap<>(); map.put("张三", new Employee("张三", 8000)); map.put("李四", new Employee("李四", 9000)); map.put("王五", new Employee("王五", 12000)); map.put("赵六", new Employee("赵六", 11000)); //把薪资低于10000的工资修改为10000 map.replaceAll((String k,Employee v) -> { if(v.getSalary()<10000){ v.setSalary(10000); } return v; }); map.forEach((String k, Employee v) -> {System.out.println(k+"->"+v);}); } public void test2(){ HashMap<String,Employee> map = new HashMap<>(); map.put("张三", new Employee("张三", 8000)); map.put("李四", new Employee("李四", 9000)); map.put("王五", new Employee("王五", 12000)); map.put("赵六", new Employee("赵六", 11000)); //把薪资低于10000的工资修改为10000 //省略了可省略部分 map.replaceAll((k,v) -> {if(v.getSalary()<10000)v.setSalary(10000);return v;}); map.forEach((k, v) -> System.out.println(k+"->"+v)); } } package com.bdit.lambda.bean; public class Employee { private String name; private double salary; public Employee(String name, double salary) { super(); this.name = name; this.salary = salary; } public Employee() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public String toString() { return "Employee [name=" + name + ", salary=" + salary + "]"; } }

    代码示例:Predicate接口

    JDK1.8时,Collecton接口增加了一下方法,其中一个如下:default boolean removeIf(Predicate<? super E> filter) 用于删除集合中满足filter指定的条件判断的。

    public void test3(){ ArrayList<Employee> list = new ArrayList<>(); list.add(new Employee("张三", 8000)); list.add(new Employee("李四", 9000)); list.add(new Employee("王五", 12000)); list.add(new Employee("赵六", 11000)); //删除那些薪资低于10000的 list.removeIf((Employee e) -> {return e.getSalary()>10000;}); list.forEach((Employee e) -> {System.out.println(e);}); } public void test4(){ ArrayList<Employee> list = new ArrayList<>(); list.add(new Employee("张三", 8000)); list.add(new Employee("李四", 9000)); list.add(new Employee("王五", 12000)); list.add(new Employee("赵六", 11000)); //删除那些薪资低于10000的 //省略了可以省略的部分 list.removeIf(e -> e.getSalary()>10000); list.forEach(e -> System.out.println(e)); }

    4、方法引用和构造器引用

    当Lambda体的实现是通过调用一个现有的方法来完成功能时,那么可以考虑再次简化代码,使用方法引用和构造器引用。

    此时要求函数式接口的抽象方法的形参列表与返回值类型与该方法(要调用的方法)的形参列表与返回值类型要对应。

    方法引用的语法格式:

    类或对象::方法名

    构造器引用的语法格式:

    类名或数组类型::new

    1、对象::实例方法名

    例如:Consumer的抽象方法void accept(T t),它的Lambda体是通过调用System.out.println(x)来完成,它们都是有参无返回值。

    示例代码:

    public void test1(){ List<String> list = Arrays.asList("hello","java","lambda","bdit"); //使用Lambda表达式 list.forEach(e -> System.out.println(e)); } public void test2(){ List<String> list = Arrays.asList("hello","java","lambda","bdit"); //使用方法引用 list.forEach(System.out::println); }

    例如:Comparator接口的抽象方法int compare(T t1,T t2),它的Lambda体是通过调用Collator文本校对器对象的compare(t1,t2)方法来完成,他们的形参列表和返回值类型完成可以对上。

    示例代码:

    public void test3(){ //使用Lambda表达式 TreeSet<String> set=new TreeSet<>((t1,t2)->Collator.getInstance().compare(t1,t2)); set.add("张三"); set.add("李四"); set.add("王五"); set.add("赵六"); set.forEach(e->System.out.println(e)); } public void test4(){ //使用方法引用 TreeSet<String> set=new TreeSet<>(Collator.getInstance()::compare); set.add("张三"); set.add("李四"); set.add("王五"); set.add("赵六"); set.forEach(System.out::println); }

    2、类::静态方法名

    例如:Supplier的抽象方法T get(),它的lambda体是通过调用Math.random()来完成的,它们都是无参有返回值。

    示例代码:

    public void test5(){ //使用Lambda表达式 Stream<Double> stream = Stream.generate(() -> Math.random()); stream.forEach(num -> System.out.println(num)); } public void test6(){ //使用方法引用 Stream<Double> stream = Stream.generate(Math::random); stream.forEach(System.out::println); }

    3、类::实例方法名

    例如:Comparator接口的抽象方法int compare(T t1,T t2),它的Lambda体是通过调用String对象的compareToIgnoreCase(String o)来完成的,而且这里调用compareToIgnoreCase方法是用compare()方法的第一个参数,而compare()方法剩下的参数正好是给compareToIgnoreCase的实参,它俩的返回值类型都是int。

    示例代码:

    public void test7(){ String[] arr = {"Hello","hello","abc","world","ABC"}; //使用generate Arrays.sort(arr, (s1,s2)-> s1.compareToIgnoreCase(s2)); System.out.println(Arrays.toString(arr)); } public void test8(){ String[] arr = {"Hello","hello","abc","world","ABC"}; //使用方法引用 Arrays.sort(arr, String::compareToIgnoreCase); System.out.println(Arrays.toString(arr)); }

    4、类名::new

    例如:在JDK1.8中增加了一个工具类Optional,它是一个容器,可以用来包装一个对象,如果所包装的对象不为null,那么通过get()方法可以获取到这个对象,如果所包装的对象是null ,调用get()方法会报异常,所以很多时候需要通过调用orElseGet(Supplier<? extends T> other) 来获取,即如果该对象存在,就返回该对象,否则返回由Supplier接口实现所提供的对象。关于Optional类的详细介绍请看我的博客Optinal类

    示例代码:

    public void test9(){ HashMap<String,Employee> map = new HashMap<>(); map.put("张三", new Employee("张三", 8000)); map.put("李四", new Employee("李四", 9000)); Optional<Employee> opt = Optional.ofNullable(map.get("王五")); //使用Lambda表达式 Employee emp = opt.orElseGet(() -> new Employee()); System.out.println(emp); } public void test10(){ HashMap<String,Employee> map = new HashMap<>(); map.put("张三", new Employee("张三", 8000)); map.put("李四", new Employee("李四", 9000)); Optional<Employee> opt = Optional.ofNullable(map.get("王五")); //使用构造器引用 Employee emp = opt.orElseGet(Employee::new); System.out.println(emp); }

    5、数组类型::new

    package com.bdit; /** * 数组的方法引用 */ public class Test13 { public static void main(String[] args) { int[]nums1=new int[10]; int[]nums2=initArray(5,s->new int[s]); System.out.println(nums2.length); //方法引用 int[]nums3=initArray(6,int[]::new); System.out.println(nums3.length); } public static int[] initArray(int length,MyArrays myArrays){ return myArrays.buildArray(length); } } @FunctionalInterface interface MyArrays{ /** * 根据length返回一个数组对象 * @param length * @return */ int[]buildArray(int length); }

    6、通过this引用成员方法

    使用this::成员方法 的语法来引用

    package com.bdit; public class Test14 { public static void main(String[] args) { } public void method1(){ show(()-> System.out.println("哈哈哈哈")); } public void method2(){ show(()->this.method3()); } public void method3(){ show(this::method2); } public void show(MyThis myThis){ myThis.test(); } } @FunctionalInterface interface MyThis{ public void test(); }
    Processed: 0.014, SQL: 8