Java新特性之Lambda表达式

Lambda表达式

函数式编程思想

之前我们学习的Java都是面向对象的思想(重点是做某一件事的执行者,谁去做),而函数式的编程思想由重心点(谁去做)变成了做事的过程

一个接口只有一个抽象方法的话:

  1. 我们之前
  • 要么是声明一个类去实现接口重写这个抽象方法,再去创建这个类去调用方法
  • 要么是使用匿名内部类,重写里面的方法再去调用方法

无论哪一种方式都需要去创建一个对象,去new一个对象,而我们主要是想要去使用这个方法,方法里面只有实参和方法体不同。再去创建对象,写方法体里面的共同不变的部分就显的多余了

  1. 现在(通过函数式编程思想)
  • 只需要使用这个方法

注意:这里说的是一个接口只有一个抽象方法的情况

什么是Lambda表达式

Oracle所发布的JDK 1.8中,加入了Lambda表达式的重量级新特性,为我们打开了新世界的大门。Java8引入了Lambda表达式之后,Java也开始支持函数式编程。

Lambda表达式不是Java最早使用的,很多语言就支持Lambda表达式,例如:C++,C#,Python,Scala等。lambda表达式其实就是实现SAM接口的语法糖,使得Java也算是支持函数式编程的语言。Lambda写的好可以极大的减少代码冗余,同时可读性也好过冗长的(啰嗦的)匿名内部类。语法糖是指使用更加方便,但是原理不变的代码语法

函数接口的概念

  • SAM接口:就是Single Abstract Method接口

  • Lambda表达式就是一个实现了SAM接口的语法格式。

啥叫函数式接口呢?

其实只要满足“SAM”特征的接口都可以称为函数式接口,都可以使用Lambda表达式。

总而言之:只有一个抽象方法的接口(抽象方法只有一个,但可以有其他static、default方法)

备注:可以使用注解@FunctionalInterface来检查是不是函数式接口,检查接口里面是不是只有一个抽象方法。(和@Override类似)

之前学过的接口已经很多了:Cloneable、Comparable、Comparator、Runnable、Iterable、Iterator、Collection、List、Queue、Deque、Set、Map、Serializable、FileFilter、FilenameFilter等,但是满足SAM特征的接口不多。

虽然Iterable和Comparable接口也只有一个抽象方法,但它们没有@FunctionalInterface注解标记,因此不算。

序号接口抽象方法SAM接口
1java.lang.Runnablepublic void run()
2java.util.Comparatorpublic int compare(T t1, T t2)
3java.io.FileFilterpublic boolean accept(File pathname)
4java.io.FilenameFilterpublic boolean accept(File dir, String name)

Lambda表达式语法

Lambda可以说就是简化函数式接口用的。将一些公共的部分给省略了,将必要的部分给保留下来。有参数和方法体。

Lambda表达式语法格式

(【形参列表】) -> {方法体}

语法格式说明:

  • (【形参列表】)它就是你要传递给原函数式接口的参数列表
  • {方法体}就是实现这个抽象方法的方法体
  • ->称为Lambda操作符(减号和大于号中间不能有空格)

例如:

public class LambdaTest {
    public static void main(String[] args) {

        Runnable runnable = new Runnable() { //原来的匿名内部类形势
            @Override
            public void run() {
                System.out.println("Runnable");
            }
        };
        
		Runnable runnable1 = () -> {System.out.println("Runnable1");}; //无参的Lambda表达式

		Integer[] arr = {1,3,5,2,6,7};
        Arrays.sort(arr, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        });

        Arrays.sort(arr,(Integer o1, Integer o2) -> {return Integer.compare(o1,o2);}); //有参的Lambda表达式
        for (Integer integer : arr) {
            System.out.println("integer = " + integer);
        }
    }
}

Lambda表达式的简化

Lambda表达可以有以下几种的语法格式,根据不同的参数列表与方法体选择的简化格式也可以不同。

  • 当{方法体}中只有一句语句时,可以省略{}和{;}
  • 当{Lambda体}中只有一句语句时,并且这个语句还是一个return语句,那么{、return、;}三者可以省略。它们三要么一起省略,要么都不省略。
  • 当Lambda表达式(形参列表)的类型已知,获取根据泛型规则可以自动推断,那么(形参列表)的数据类型可以省略。
  • 当Lambda表达式(形参列表)的形参个数只有一个,并且类型已知或可以自动推断,则形参的数据类型和()可以一起省略,但是形参名不能省略。
  • 当Lambda表达式(形参列表)是空参时,()不能省略

演示代码:

  1. 当{方法体}中只有一句语句时,可以省略{}和{;}
Runnable runnable2 = () -> System.out.println("Runnable1");
  1. 当{方法体}中只有一句语句时,并且这个语句还是一个return语句,那么{return;}三者可以省略。它们三要么一起省略,要么都不省略。
Integer[] arr = {1,3,5,2,6,7};
Arrays.sort(arr,(Integer o1, Integer o2) -> Integer.compare(o1,o2));
  1. 当Lambda表达式(形参列表)的类型已知,获取根据泛型规则可以自动推断,那么(形参列表)的数据类型可以省略。
Integer[] arr = {1,3,5,2,6,7};
Arrays.sort(arr,( o1,  o2) -> Integer.compare(o1,o2));
  1. 当Lambda表达式(形参列表)的形参个数只有一个,并且类型已知或可以自动推断,则形参的数据类型和()可以一起省略,但是形参名不能省略。
		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
		
        list.forEach(new Consumer<Integer>() { //使用匿名内部类重写accept方法
            @Override
            public void accept(Integer integer) {
                System.out.println("Integer = " + integer);
            }
        });

		list.forEach( integer -> System.out.println("integer = " + integer));//Lambda表达式

  1. 当Lambda表达式(形参列表)是空参时,()不能省略
Runnable runnable1 = () -> {System.out.println("Runnable1");}; //无参的Lambda表达式

Java8之后引入的函数式接口

Java8在java.util.function新增了很多函数式接口:主要分为四大类消费型供给型判断型功能型。基本可以满足我们的开发需求。当然你也可以定义自己的函数式接口。

1、消费型接口

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

序号接口名抽象方法描述
1Consumervoid accept(T t)接收一个对象用于完成功能
2BiConsumer<T,U>void accept(T t, U u)接收两个对象用于完成功能
3DoubleConsumervoid accept(double value)接收一个double值
4IntConsumervoid accept(int value)接收一个int值
5LongConsumervoid accept(long value)接收一个long值
6ObjDoubleConsumervoid accept(T t, double value)接收一个对象和一个double值
7ObjIntConsumervoid accept(T t, int value)接收一个对象和一个int值
8ObjLongConsumervoid accept(T t, long value)接收一个对象和一个long值

已知在JDK1.8中java.lang.Iterable接口中增加了一个默认方法:

  • public default void forEach(Consumer<? super T> action) 该方法功能是遍历Collection集合,并将传递给action参数的操作代码应用在每一个元素上。

因为Collection接口继承了Iterable接口,这就意味着所有Collection系列的接口都包含该方法。

public class TestConsumer {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("java","c","python","c++","VB","C#");
        //匿名内部类形式
        /*list.forEach(new Consumer<String>() {
            @Override
            public void accept(String string) {
                System.out.println("String= " + string);
            }
        });*/
        //Lambda表达式形式
        list.forEach(string -> System.out.println(string ));
    }
}
2、供给型接口

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

序号接口名抽象方法描述
1SupplierT get()返回一个对象
2BooleanSupplierboolean getAsBoolean()返回一个boolean值
3DoubleSupplierdouble getAsDouble()返回一个double值
4IntSupplierint getAsInt()返回一个int值
5LongSupplierlong getAsLong()返回一个long值
public class TestSupplier {
    public static void main(String[] args) {
    	//匿名内部类形式
        /*Supplier<String> supplier = new Supplier<String>() {
            @Override
            public String get() {
                return "Java";
            }
        };*/
        //Lambda表达式形式
        Supplier<String> supplier2 = () -> "Java";
        System.out.println("String = " + supplier2.get());
    }
}
3、判断型接口

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

序号接口名抽象方法描述
1Predicateboolean test(T t)接收一个对象
2BiPredicate<T,U>boolean test(T t, U u)接收两个对象
3DoublePredicateboolean test(double value)接收一个double值
4IntPredicateboolean test(int value)接收一个int值
5LongPredicateboolean test(long value)接收一个long值

已知:JDK1.8时,Collecton接口增加了一下方法,其中一个如下:

  • public default boolean removeIf(Predicate<? super E> filter) 用于删除集合中满足filter指定的条件判断的。
public class TestPredicate {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");
        list.add("study");
        list.add("ok");
        list.add("yes");

		//匿名内部类形式
        /*list.removeIf(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("o");
            }
        });*/
        //Lambda表达式形式
        list.removeIf(s->s.contains("o"));
        list.forEach(s -> System.out.println("s = " + s));
    }
}
4、功能型接口

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

序号接口名抽象方法描述
1Function<T,R>R apply(T t)接收一个T类型对象,返回一个R类型对象结果
2UnaryOperatorT apply(T t)接收一个T类型对象,返回一个T类型对象结果
3DoubleFunctionR apply(double value)接收一个double值,返回一个R类型对象
4IntFunctionR apply(int value)接收一个int值,返回一个R类型对象
5LongFunctionR apply(long value)接收一个long值,返回一个R类型对象
6ToDoubleFunctiondouble applyAsDouble(T value)接收一个T类型对象,返回一个double
7ToIntFunctionint applyAsInt(T value)接收一个T类型对象,返回一个int
8ToLongFunctionlong applyAsLong(T value)接收一个T类型对象,返回一个long
9DoubleToIntFunctionint applyAsInt(double value)接收一个double值,返回一个int结果
10DoubleToLongFunctionlong applyAsLong(double value)接收一个double值,返回一个long结果
11IntToDoubleFunctiondouble applyAsDouble(int value)接收一个int值,返回一个double结果
12IntToLongFunctionlong applyAsLong(int value)接收一个int值,返回一个long结果
13LongToDoubleFunctiondouble applyAsDouble(long value)接收一个long值,返回一个double结果
14LongToIntFunctionint applyAsInt(long value)接收一个long值,返回一个int结果
15DoubleUnaryOperatordouble applyAsDouble(double operand)接收一个double值,返回一个double
16IntUnaryOperatorint applyAsInt(int operand)接收一个int值,返回一个int结果
17LongUnaryOperatorlong applyAsLong(long operand)接收一个long值,返回一个long结果
18BiFunction<T,U,R>R apply(T t, U u)接收一个T类型和一个U类型对象,返回一个R类型对象结果
19BinaryOperatorT apply(T t, T u)接收两个T类型对象,返回一个T类型对象结果
20ToDoubleBiFunction<T,U>double applyAsDouble(T t, U u)接收一个T类型和一个U类型对象,返回一个double
21ToIntBiFunction<T,U>int applyAsInt(T t, U u)接收一个T类型和一个U类型对象,返回一个int
22ToLongBiFunction<T,U>long applyAsLong(T t, U u)接收一个T类型和一个U类型对象,返回一个long
23DoubleBinaryOperatordouble applyAsDouble(double left, double right)接收两个double值,返回一个double结果
24IntBinaryOperatorint applyAsInt(int left, int right)接收两个int值,返回一个int结果
25LongBinaryOperatorlong applyAsLong(long left, long right)接收两个long值,返回一个long结果

已知,java.util.List接口在Java8版本中新增了一个方法:

  • default void replaceAll(UnaryOperator operator)将该列表的每个元素替换为将该运算符应用于该元素的结果。
    public static void main(String[] args) {
    	ArrayList<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");
        list.add("study");
        list.add("ok");
        list.add("yes");

		//匿名内部类形式
        /*list.replaceAll(new UnaryOperator<String>() {
            @Override
            public String apply(String s) {
                return s.substring(0,1).toUpperCase()+s.substring(1);
            }
        });*/
        //Lambda表达式形式
        list.replaceAll(s -> s.substring(0,1).toUpperCase()+s.substring(1));
        list.forEach(s-> System.out.println("s = " + s));
    }

自定义函数式接口

只要确保接口中有且仅有一个抽象方法即可:

@FunctionalInterface
修饰符 interface 接口名称 {
    public abstract 返回值类型 方法名称(可选参数信息);
    // 其他非抽象方法内容
}

接口当中抽象方法的 public abstract 是可以省略的

例如:声明一个计算器Calculator<T,R>接口,内含抽象方法calculate可以对两个参数进行计算,并返回结果。其中T是参数类型,R是返回值类型。

@FunctionalInterface
public interface Calculator<T,R> {
    R calculate(T a, T b);
}

示例代码:

public class LambdaGrammarSimple {
    @Test
    public void test01() {
        //使用Lambda表达式实现Calculator接口,求两个整数的和的功能
        Calculator<Integer,Integer> c1 = (Integer a, Integer b) -> {return a+b;};
        System.out.println(c1.calculate(5, 2));

        Calculator<Integer,Integer> c2 = (Integer a, Integer b) ->  a+b;
        System.out.println(c2.calculate(5, 2));

        Calculator<Integer,Integer> c3 = (a,  b) -> a+b;
        System.out.println(c3.calculate(5, 2));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

黑妖问路

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

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

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

打赏作者

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

抵扣说明:

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

余额充值