Java8 Lambda表达式

Lambda简介

从Java8开始,Java引入了另外一个语法糖-Lambda表达式,Lambda表达式可以由编译器推断并转换包装为常规的代码,因此我们可以使用更少的代码来实现同样的功能。

Lambda的基本语法表示如下:

基本语法:

(parameters) -> expression

(parameters) ->{ statements; }

Lambda表达式是Java SE8的一个重要新特性,它允许我们通过表达式来代替功能接口。Lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体。

虽然Lambda表达式可以对某些接口进行简单实现,但是并不是所有的接口都可以使用Lambda表达式,Lambda 表达式规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法。

Java8中有另一个新特性:接口中可以提供方法的默认实现,使用default修饰的方法就是方法的默认实现,所以在外部看来就不是必须被实现的方法了,因此也不影响Lambda表达式的使用。

一般将可以使用Lambda表达式的接口称为函数式接口,函数式接口可以使用FunctionalInterface注解标记,FunctionalInterface注解具有一下特点。

  • 该注解只能标记在"有且仅有一个抽象方法"的接口上。
  • JDK8接口中的静态方法和默认方法,都不算是抽象方法。
  • 接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么也不算抽象方法。
  • 该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。

如下两个接口都可以归类到函数式接口,因此都可以使用Lambda表达式表示。

@FunctionalInterface
public interface MyFunctionalInterface {
    void method();
	
	// Object中的方法
    int hashCode();
}

@FunctionalInterface
public interface MyFunctionalInterface {
    void method();

    default void method01(){
        // ...
    }

    default void method02(){
        // ...
    }
}

Lambda示例

这里给出6个接口,后面的示例基于这几个接口演示。

// 无参无返回值
@FunctionalInterface
public interface NoReturnNoParam {
    void method();
}

// 一个参数无返回
@FunctionalInterface
public interface NoReturnOneParam {
    void method(int a);
}

// 多参数无返回
@FunctionalInterface
public interface NoReturnMultiParam {
    void method(int a, int b);
}

// 无参有返回
@FunctionalInterface
public interface ReturnNoParam {
    int method();
}

// 一个参数有返回值
@FunctionalInterface
public interface ReturnOneParam {
    int method(int a);
}

// 多个参数有返回值
@FunctionalInterface
public interface ReturnMultiParam {
    int method(int a, int b);
}

public class MainTest {
    public static void main(String[] args) {
        //无参无返回
        NoReturnNoParam noReturnNoParam = () -> {
            System.out.println("NoReturnNoParam");
        };
        noReturnNoParam.method();

        //一个参数无返回
        NoReturnOneParam noReturnOneParam = (int a) -> {
            System.out.println("NoReturnOneParam param:" + a);
        };
        noReturnOneParam.method(6);

        //多个参数无返回
        NoReturnMultiParam noReturnMultiParam = (int a, int b) -> {
            System.out.println("NoReturnMultiParam param:" + "{" + a + "," + +b + "}");
        };
        noReturnMultiParam.method(6, 8);

        // 无参有返回值
        ReturnNoParam returnNoParam = () -> {
            System.out.print("ReturnNoParam");
            return 1;
        };
        int res = returnNoParam.method();
        System.out.println("return:" + res);

        // 一个参数有返回值
        ReturnOneParam returnOneParam = (int a) -> {
            System.out.println("ReturnOneParam param:" + a);
            return 1;
        };
        int res2 = returnOneParam.method(6);
        System.out.println("return:" + res2);

        // 多个参数有返回值
        ReturnMultiParam returnMultiParam = (int a, int b) -> {
            System.out.println("ReturnMultiParam param:" + "{" + a + "," + b + "}");
            return 1;
        };
        int res3 = returnMultiParam.method(6, 8);
        System.out.println("return:" + res3);
    }
}

Lambda语法简化。

public class MainTest {
    public static void main(String[] args) {
        //1.简化参数类型,可以不写参数类型,但是必须所有参数都不写
        NoReturnMultiParam lambda01 = (a, b) -> {
            System.out.println("简化参数类型");
        };
        lambda01.method(1, 2);

        //2.简化参数小括号,如果只有一个参数则可以省略参数小括号
        NoReturnOneParam lambda02 = a -> {
            System.out.println("简化参数小括号");
        };
        lambda02.method(1);

        //3.简化方法体大括号,如果方法条只有一条语句,则可以省略方法体大括号
        NoReturnNoParam lambda03 = () -> System.out.println("简化方法体大括号");
        lambda03.method();

        //4.如果方法体只有一条语句,并且是 return 语句,则可以省略方法体大括号
        ReturnOneParam lambda04 = a -> a + 3;
        System.out.println(lambda04.method(5));

        ReturnMultiParam lambda05 = (a, b) -> a + b;
        System.out.println(lambda05.method(1, 1));
    }
}

方法引用

Lambda表达式的使用不仅引入了运算符->,而且还引入了另外一个新的运算符::。双冒号::运算符在Java8中被称为方法引用(method reference),方法引用是与Lambda表达式相关的一个重要特性。

我们用Lambda表达式来实现匿名方法。但有些情况下,我们用Lambda表达式仅仅是调用一些已经存在的方法,除了调用动作外,没有其他任何多余的动作,在这种情况下,我们倾向于通过方法名来调用它,而Lambda表达式可以帮助我们实现这一要求,它使得Lambda在调用那些已经拥有方法名的方法的代码更简洁、更容易理解。方法引用可以理解为Lambda表达式的另外一种表现形式。

方法引用一般分为静态方法引用、实例方法引用、构造器方法引用。

静态方法引用和对象方法引用

public class MainTest {
    public static void main(String[] args) {
		// 静态方法引用
        ReturnOneParam lambda01 = MainTest::addByTwo;
        System.out.println(lambda01.method(5));

		// 对象方法引用
		MainTest mainTest= new MainTest();
        ReturnMultiParam lambda02 = mainTest::addMethod;
        System.out.println(lambda02.method(1, 1));
    }

    private static int addByTwo(int x) {
        return x + 2;
    }

    private int addMethod(int x, int y) {
        return x + y;
    }
}

但是在某个场景下,实例方法引用与静态方法引用使用方式却是一样的。若Lambda参数列表中的第一个参数是方法的参数调用者,而第二个参数是实例方法的参数时,可以使用实例方法引用。

BiPredicate<String, String> bp = (x, y) -> x.equals(y);
BiPredicate<String, String> bp01 = String::equals;
boolean test = bp01.test("xy", "xx");
System.out.println(test);

构造器方法引用

public class User {
    private String username;

    public User(String username) {
        this.username = username;
    }
}

@FunctionalInterface
public interface BeanFactory {
    User create(String name);
}

// 构造器方法引用    
BeanFactory beanFactory = User::new;
User user = beanFactory.create("hello world");

其它方法引用示例

String[] array = {"aaa", "bbb", "ccc"};
List<String> list = Arrays.asList(array);
list.forEach(System.out::println);

Function<Integer, User[]> function = User[]::new;
User[] array = function.apply(4);//这里的4是数组的大小
for(User e:array){
	System.out.println(e);//如果输出的话,会输出4个空对象(null)
}

Supplier<List<User>> userSupplier = ArrayList::new;
List<User> user = userSupplier.get();

参考资料

Lambda 表达式有何用处?如何使用?

Lambda表达式详解

Java8新特性之二:方法引用

Kotlin——高级篇(一):Lambda表达式详解

评论

您确定要删除吗?删除之后不可恢复