java8 : lambda 表达式

JAVA 8 lambda 表达式翻译

函数接口

​ 函数接口是指: 一个接口中除了默认方法以及静态方法或者非Object 类的方法外只有一个方法,这个接口就 可以被定 义为函数接口

java8 中新增的接口

  • Predicate——接收 T 并返回 boolean
  • Consumer——接收 T,不返回值
  • Function——接收 T,返回 R
  • Supplier——提供 T 对象(例如工厂),不接收值
  • UnaryOperator——接收 T 对象,返回 T
  • BinaryOperator——接收两个 T,返回 T

已经存在的接口

  • java.lang.Runnable —— 无入参,无返回值,方法名为run
  • java.util.concurrent.Callable —— 无入参,返回V ,方法名为call
  • java.security.PrivilegedAction —— 无入参,返回T ,方法名为run
  • java.util.Comparator—— 入参为T1,T2返回值为int,方法名为 compare
  • java.io.FileFilter —— 入参为File 返回值为boolean ,方法名为accept
  • java.beans.PropertyChangeListener 入参为PropertyChangeEvent 无返回值 方法名propertyChange


表达式

一个例子

//使用匿名内部类的方式
Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("--------");
    }
});
//使用表达式
Thread thread1 = new Thread(()-> System.out.println("----------"));

完整表达式

(Object o1,Object o2)-> {return o1.equales(02)}
  1. ()->{} 括号内的参数传入方法体 () 参数 ;->传入{}方法体

  2. ()内的参数,如果参数类型可以被虚拟机推导出来,那么参数可以被省略

    (o1,o2)->{return o1.equales(o2)}

  3. { } 如果方法体只有一条语句 ,{}就可以省略
    (o1,o2)-> o1.equales(o2)

  4. 函数体既可以是一个表达式,也可以是一个语句块:

  • 表达式:表达式会被执行然后返回执行结果。
  • 语句块:语句块中的语句会被依次执行,就像方法中的语句一样

    return 语句会把控制权交给匿名方法的调用者

    breakcontinue只能在循环中使用

    如果函数体有返回值,那么函数体内部的每一条路径都必须有返回值

  1. 表达式函数体适合小型 lambda 表达式,它消除了 return 关键字,使得语法更加简洁。

  2. 对于函数体内部,只是方法的引用可以用如下形式:

  • 静态方法引用:ClassName::methodName
  • 实例上的实例方法引用:instanceReference::methodName
  • 超类上的实例方法引用:super::methodName
  • 类型上的实例方法引用:ClassName::methodName
  • 构造方法引用:Class::new
  • 数组构造方法引用:TypeName[]::new

注意点

  • 表达式内部不会从超类继承任何变量
  • 表达式不会引入一个新的作用域
  • 表达式函数体内部和外部的变量具有相同的语义
  • 表达式函数体内部的this与外部语义相同
  public class Hello {
    Runnable r1 = () -> { System.out.println(this); }
    Runnable r2 = () -> { System.out.println(toString()); }

    public String toString() {  return "Hello, world"; }

    //会打印两遍 helloworld
    public static void main(String... args) {
      new Hello().r1.run();
      new Hello().r2.run();
    }
  }
  • 重载解析会为一个给定的方法调用(method invocation)寻找最合适的方法声明(method declaration)。由于不同的声明具有不同的签名,当 lambda 表达式作为方法参数时,重载解析就会影响到 lambda 表达式的目标类型。编译器会通过它所得之的信息来做出决定。如果 lambda 表达式具有 显式类型(参数类型被显式指定),编译器就可以直接 使用lambda 表达式的返回类型;如果lambda表达式具有 隐式类型(参数类型被推导而知),重载解析则会忽略 lambda 表达式函数体而只依赖 lambda 表达式参数的数量。

  • 返回函数的表达式 Supplier<Runnable> c = () -> () -> { System.out.println("hi"); };

  • 转型表达式(Cast expression)可以显式提供 lambda 表达式的类型,这个特性在无法确认目标类型时非常有用:

    // Object o = () -> { System.out.println("hi"); }; 这段代码是非法的
    Object o = (Runnable) () -> { System.out.println("hi"); };
    
  • 下面的代码在 Java SE 7 是非法的,但在 Java SE 8 中是合法的:

    List<String> ls = Collections.checkedList(new ArrayList<>(), String.class);
    Set<Integer> si = flag ? Collections.singleton(23) : Collections.emptySet();  
    
  • 表达式内部不能够修改外部的局部变量