写在前面

本文主要是简单介绍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,真的很方便并且效率更高。


阿基米德
1 声望0 粉丝