Lambda定义

lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。

Lambda语法

lambda表达式由以下三部分组成:参数,箭头(->)以及一个表达式。如果代码要完成的计算无法放在一个表达式中,就可以像写方法一样,把这些代码放在{}中,并包含显式的return语句。

下面我们来分不同的情况来了解Lambda表达式的语法。

  1. 即使Lambda表达式没有参数,仍要提供空括号,就像无参数方法一样。
  1. Runnable runnable = () -> System.out.println("Hello World!");
  1. 如果可以推导出一个lambda表达式的参数类型,则可以忽略其类型。
  1. //实际上和(String first, String second)一致
  2. Comparator<String> comp =
  3. (first, second) -> first.length() - second.length();
  1. 如果方法只有一个参数,而且这个参数的类型可以推导得出,甚至可以省略小括号。
ActionListener listener =
     event -> System.out.println("The time is " + new Date());
  1. 无需指定lambda表达式的返回类型。lambda表达式的返回类型总是会由上下文推导得出。
(String first,String second) -> first.length() - second.length()

注意:

如果一个lambda表达式只在某些分支返回一个值,而在另外一些分支不返回值,这样是不合法的。例如:

(int x) ->{if (x >=0) return 1;}

函数式接口

对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式。这种接口称为函数式接口。

下面我们就Comparator来进行一次详细的分析:

Array.sort(words, (first,second)->first.length - second.length());

Array.sort方法的第二个参数需要一个Comparator的实例,Comparator就是只有一个方法的接口,所以可以提供一个lambda表达式来代替。

在底层,Arrays.sort方法会接收实现了Comparator的某个类的对象。在这个对象上调用compare方法会执行这个lambda表达式的体。这些对象和类的管理完全取决于具体实现,与传统的内联类相比,这样可能会高效的多。

最好把lambda表达式看作是一个函数,而不是一个对象,在Java中,对lambda表达式所能做的也只能是转换为函数式接口。

如果我们在实际的开发中想使用这种看起来高大上的方式,首先我们需要为这个lambda表达式建立一个特定的函数式接口,并记住这个函数式接口的用途。

PS:

在Java API中的 java.util.function 中为我们预定义了一些函数式接口,我们可以根据个人需求去选用。

方法引用

有时,可能已经有现成的方法可以完成你想要传递到其他代码的某个动作。这时就会用到方法引用,方法引用的主要特征在于用::操作符分隔方法名与对象或者类名。

List<Person> personList = new ArrayList<>();
        Person p1 = new Person("Vi", "123456", new Date(), 13027760730L, 1000000.00);
        Person p2 = new Person("Lizzie", "123456", new Date(), 13027760307L, 100000.00);

        personList.add(p1);
        personList.add(p2);
        //forEach方法的参数是Comsumer接口,这个接口是一个函数式接口,有一个名为accpet的用于接收类型的接口方法(函数)。
        personList.forEach((person -> System.out.println(person + ";")));
        personList.forEach(System.out::println);

在方法引用中,主要分为以下三种情况:

  1. 对象:: 实例方法
  2. 类:: 静态方法
  3. 类:: 实例方法

在前2种情况中,方法引用等价于提供方法参数的lambda表达式。

System.out::println 等价于 x -> System.out.println(x)(第一种情况)

Math::pow 等价于(x, y) -> Math.pow(x, y)

String:: compareToIgnoreCase 等价于 (x,y)-> x.compareToIgnoreCase(y)

方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法。它的方法返回值应该与函数式接口的返回值保持一致,起到的作用是使用现有的方法代替了lambda表达式的实现(即函数式接口的实例)。而具体的参数仍由调用这个方法引用的方法提供。且参数个数(类型)和函数式接口的参数个数要保持一致

如果有多个同名的重载方法,编译器就会尝试从上下文中找出你指的那一个方法。例如,Math.max方法有两个版本,一个用于整数,一个用于double值。选择哪一个版本取决于Math::max转换为哪个函数式接口的方法参数。类似于lambda表达式,方法引用不能独立存在,总是会转换成函数式接口的实例。

PS

可以在方法中使用this和super,比如this::equals等价于 x -> this.equals(x),super::equals会以this为目标去调用给定方法的超类版本。有关构造器的引用我们会在学习 Java 8 的另一个特性—stream中去学习。


公众号

扫码或微信搜索 Vi的技术博客,关注公众号,不定期送书活动各种福利~

Java基础系列(二十六):Lambda基础 - 图1