Lambda表达式使用介绍

30次阅读

共计 7630 个字符,预计需要花费 20 分钟才能阅读完成。

写在前面

本文主要是简单介绍 Lambda 表达式和函数式接口的使用方法,并不涉及原理,希望初学者看完之后可以在日常开发中灵活运用 Lambda 表达式编程

Lambda 的特性

Java 8 增加了一个全新语言级别的功能,称为 Lambda 表达式。允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理存储在变量中

Lambda 表达式语法

Lambda 表达式由参数列表和方法体组成:`(type1 arg1,type2 arg2,...) -> {body;};` 参数之间用 `,` 分隔
  • 1、没有入参时,用 () 空括号表示。如:() -> {body};
  • 2、入参只有一个时,可以有两种表示方式:

      1) (String s) -> {body};
      2)s -> {body}; 省略了参数的类型,Lambda 会根据上下文推断出参数类型,并且不需要括号包围
    
  • 3、有多个入参时:(String name,int age,boolean flag) -> {body}

       也可以省略参数类型直接写成这样:`(name,age,flag) -> {body}` 参数列表必须有 `()` 包围
  • 4、当方法体只有一条语句时,可以省略{}。如:() -> System.out.println("hello world");
 - 一些例子:1、()-> System.out.println("Lambda 表达式");
2、(String s) -> System.out.println(s);
3、s -> return s;

4、(String s,int num) -> {if(num >= 10){return s.toUpperCase();
    }
    return s.toLowerCase();};
5、(age,name,flag) -> 42;

Lambda 表达式不能单独使用,需要绑定函数式接口才能使用

函数式接口

函数式接口 (Functional Interface) 是 Java 8 对一类特殊类型的接口的称呼。这类接口抽象方法的数量只有一个,并且使用了 @FunctionalInterface 进行注解。需要注意的是,只有一个抽象方法的接口,即使没有 @FunctionalInterface 进行注解,编译器也会把它当作函数式接口。

特殊的是 java.util.Comparator<T> 接口具有两个抽象方法,如下:

int compare(T o1, T o2);
boolean equals(Object obj);

但它仍然是函数式接口,为什么呢?

如果接口声明了一个覆盖 java.lang.Object 公共方法的抽象方法,那么这个方法不会计入接口的抽象方法计数中,因为该接口的任何实现都将具有来自 java.lang.Object 或其他地方的针对于该方法的实现。java.util.Comparator<T> 接口中的 equals(Object obj)方法是 java.lang.Object 中的一个 public 方法的声明,不在抽象方法的计数中

实际上 Lambda 表达式就是对函数式接口中唯一的抽象方法的实现,所以 Lambda 表达式的参数需要和函数式接口抽象方法的参数列表一致。

在 jdk8 中,引入了一个新的包 java.util.function, 提供了一系列的函数式接口,这个包中的接口大致可以分为以下四类:
Function<T,R>: 接收参数,并返回结果,抽象方法为 R apply(T t)
Consumer<T>: 接收参数,无返回结果, 抽象方法为 void accept(T t)
Supplier<T>: 不接收参数,但返回结果,抽象方法为 T get()
Predicate<T>: 接收参数,返回 boolean 值,抽象方法为 boolean test(T t)

可以根据需要选择不同的函数式接口,不需要每次自定义函数式接口使用

Lambda 表达式和函数式接口的实例应用

1、获得函数式接口的实例对象,进行方法的调用

// 自定义函数式接口
@FunctionalInterface
public interface TestFunctionInterface {
    // 函数式接口的唯一抽象方法,需要 Lambda 表达式实现
    String testFunction(String a,int b);
    
}

// 测试类
public class Client {public static void main(String[] args) {
      // 使用 Lambda 表达式实现 TestFunctionInterface 的抽象方法,并且得到实例对象
      TestFunctionInterface testFunctionInterface = (String name,int age)->{if(age==10){return name.toUpperCase();
      }
      return "NO NAME!";
    };

    // 通过 testFunctionInterface 调用 testFunction()方法并传入参数,得到返回的结果 result
    String result = testFunctionInterface.testFunction("David",10);
    System.out.println(result);

    }

}


打印结果:DAVID

----------
2、函数式接口作为某个方法的参数使用

// 自定义函数式接口
@FunctionalInterface
public interface TestFunctionInterface {
    // 函数式接口的唯一抽象方法,需要 Lambda 表达式实现
    String testFunction(String a,int b);
    
}


// 测试类
public class Client {public static void main(String[] args) {
        // 调用 test 方法
        test("steven",20,(name,age) -> {if(age==10){return name.toUpperCase();
            }
            return "NO NAME!";
            }
        );

    }
    
    // 函数式接口对象作为参数的方法
    public static void test(String name,int age,TestFunctionInterface testFunctionInterface){String result = testFunctionInterface.testFunction(name,age);
        System.out.println(result);
    }

}

打印结果:NO NAME!

特别注意:以上两种方法使用 Lambda 表达式时,Lambda 的参数和返回值必须要和函数式接口的抽象方法保持一致

方法引用

Lambda 表达式使用 :: 来调用特定的已存在方法作为函数式接口唯一抽象方法的实现,可以使代码更具可读性

1. 静态方法

Lambda :(arg0,arg1,arg2,…) -> ClassName.staticMethod(arg0,arg1,arg2,…)
方法引用:ClassName::staticMethod

`TestStaticMethod::testStaticMethod` 即调用 `TestStaticMethod` 的静态方法 `String testStaticMethod(int age,boolean flag)` 作为函数式接口抽象方法的实现,调用的方法的参数类型和返回值需要和抽象方法保持一致
@FunctionalInterface
public interface TestFunctionInterface {String testFunction(int i);

}

public class TestStaticMethod {public static String testStaticMethod(int age,boolean flag){if(age >10 && flag){return "SUCCESS";}

        return "ERROR";
    }
}


public class Client {public static void main(String[] args) {//TestStaticMethod::testStaticMethod 相当于 Lambda 表达式:(int age,boolean flag) -> TestStaticMethod.testStaticMethod(age,flag); 会将 Lambda 的参数按顺序传入调用的方法中,类型不匹配会报错
        TestFunctionInterface testFunctionInterface = TestStaticMethod::testStaticMethod;
        String result = testFunctionInterface.testFuction(26,true);
        System.out.println(result);


    }

}


打印结果:SUCCESS

2. 指向任意类型的实例方法引用

Lambda:(arg0,arg1,arg2,…) -> arg0.instanceMethod(arg1,arg2,…)
方法引用:ClassName.instanceMethod (注意:arg0 是 ClassName 类型的对象)

/**
    1) 函数式接口抽象方法只有一个参数
*/
@FunctionalInterface
public interface TestFunctionInterface {int testFuction(String s);

}


public class Client {public static void main(String[] args) {// 方法引用:String::length 等价于 Lambda 表达式:(arg0) -> arg0.length()
        // 实际上调用参数对象 arg0 的 length()方法作为抽象方法的方法体实现,arg0.length()的返回值作为抽象方法的返回值 
        TestFunctionInterface testFunctionInterface = String::length;
        System.out.println(testFunctionInterface.testFuction("king"));
    }

}

打印结果:4

/**
    2) 函数式接口抽象方法有多个参数
*/
@FunctionalInterface
public interface TestFunctionInterface {int testFuction(TestMethodRef testMethodRef, String name, int age);

}

public class TestMethodRef {public int testMethodRef(String name,int age){return name.length()+age;
    }
}

public class Client {public static void main(String[] args) {//TestMethodRef::testMethodRef 等价于 (arg0,arg1,arg2) -> arg0.testMethodRef(arg1,arg2)
        // 编译器只会调用第一个参数 arg0 的 testMethodRef(arg1,arg2)方法,并且把余下的 arg1,arg2 按原来的顺序作为参数传入该方法中,类型不匹配会报错
        TestFunctionInterface testFunctionInterface = TestMethodRef::testMethodRef;
        int result = testFunctionInterface.testFuction(new TestMethodRef(),"king",12);
        System.out.println(result);

    }

}

打印结果:16

3. 指向现有对象的实例方法引用
@FunctionalInterface
public interface TestFunctionInterface {char testFuction(int index);

}

public class Client {public static void main(String[] args) {
        String content="hello";
        //content::charAt 等价于 (int index) -> content.charAt(index);
        TestFunctionInterface testFunctionInterface = content::charAt;
        System.out.println(testFunctionInterface.testFuction(0));
    }

}    

打印结果:h

Stream API

java.util.stream.Stream 接口 和 Lambda 表达式一样,都是 Java 8 新引入的。所有 Stream 的操作必须以 Lambda 表达式为参数。Stream(流)是一个来自数据源的元素队列并支持聚合操作, 它将处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选,排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作 (terminal operation) 得到前面处理的结果,这里简单介绍一下常用的 Stream API

  • forEach 提供对 stream 流元素的迭代功能
public class Client {public static void main(String[] args) {List<Info> list = new ArrayList<>();
        list.add(new Info("first", 1));
        list.add(new Info("second", 2));
        list.add(new Info("third", 3));
        list.add(new Info("four", 4));
        list.add(new Info("five", 5));
        //list.stream()创建 Stream 流,调用 forEach 打印所有的 name
        list.stream().forEach(t->System.out.println(t.name));
    }

}
打印结果:first
second
third
four
five
  • filter 设置过滤限制

public class Client {public static void main(String[] args) {List<Info> list = new ArrayList<>();
        list.add(new Info("first", 1));
        list.add(new Info("second", 2));
        list.add(new Info("third", 3));
        list.add(new Info("four", 4));
        list.add(new Info("five", 5));
        // 针对 age 进行过滤
        list.stream().filter(t->t.age>3).forEach(t->System.out.println(t.name));
    }

}


打印结果:four
five
  • map 将流元素映射到不同的结果
public class Client {public static void main(String[] args) {List<Info> list = new ArrayList<>();
        list.add(new Info("first", 1));
        list.add(new Info("second", 2));
        list.add(new Info("third", 3));
        list.add(new Info("four", 4));
        list.add(new Info("five", 5));
        // 将 Info 对象映射成 String 对象
        List<String> names=list.stream().map(t->t.name).collect(Collectors.toList());
    }

}
  • limit 获取指定数量的流元素
Stream.of(1,2,3,4,5,6,7).limit(3).forEach(System.out::println);
打印结果:1
2
3
  • skip 忽略指定数量的流元素
// 忽略前 4 个流元素
Stream.of(1,2,3,4,5,6,7).skip(4).forEach(System.out::println);

打印结果:5
6
7
  • sorted 对流进行排序
public class Client {public static void main(String[] args) {List<Info> list = new ArrayList<>();
        list.add(new Info("first", 1));
        list.add(new Info("second", 2));
        list.add(new Info("third", 3));
        list.add(new Info("four", 4));
        list.add(new Info("five", 5));
        list.stream().sorted(new Comparator<Info>() {
            @Override
            // 根据 age 降序排列
            public int compare(Info o1, Info o2) {return o2.age-o1.age;}
        }).forEach(t->System.out.print(t.name+" "));
    }

}

打印结果:five four third second first 

  • findFirst 返回第一个元素
//findFirst()返回 Optional 对象,需要调用 Optional.get()方法获取值
int value=Stream.of(1,2,3,4,5,6,7).findFirst().get();
System.out.println(value);

打印结果:1
  • anyMatch 匹配任意一个就返回 true
//stream 流中只要有一个元素的值等于 2,就返回 true
boolean value=Stream.of(1,2,3,4,5,6,7).anyMatch(t->t==2);
System.out.println(value);

打印结果:true
  • allMatch 匹配所有元素才返回 true
//stream 流中所有元素的值都等于 2,才返回 true
boolean value=Stream.of(1,2,3,4,5,6,7).allMatch(t->t==2);
System.out.println(value);

打印结果:false
  • collect 将 Stream 转换为集合
List list=Stream.of(1,2,3,4,5,6,7).collect(Collectors.toList());
Set set = Stream.of(1,2,3,4,5,6,7).collect(Collectors.toSet());

最后

希望大家在开发中可以多使用 Lambda 表达式、函数式接口和 Stream API,真的很方便并且效率更高。

正文完
 0