1.1.4Java 匿名类和λ表达式

Java lambda表达式是(函数接口的)匿名类的语法糖。
Java的λ表达式并不是没有名字而是省略了名字。

Java的lambda表达式是人妖。程序员则应该把Java的lambda表达式“看成”函数。

[经过调整,第1章1.1回调机制介绍匿名类和λ表达式。关于λ表达式是人妖,则在第2章中介绍, 2.3.1函数数据类型Vs. 函数类型签名 ]


【实验1:回调机制的实现】下层模块/框架中需要设计一个op函数,对两个参数值进行“某种”操作后返回一个值。

//例程 1-3  Java中的某种操作
package chap1.callBack;
@FunctionalInterface 
public interface BinaryOP {//二元操作
      int op(int a, int b);
}

package chap1.callBack;
public class AddOp implements BinaryOP {
    @Override public int op(int m,int n){  
        return m+n;  
    }
}

Java语言中使用类层次/动态绑定/多态来提供回调函数/支持代码。当应用程序Test需要使用BinaryOP时,可以编写BinaryOP的、有单独的.java文件的实现类如AddOp,以提供支持代码;如果实现类不具有复用性,或者说不具有独立存在的价值,可以编写BinaryOP的匿名类。匿名类在定义其类体的同时,创建了自己的对象,并向上造型为父类型。匿名类没有名字,其类型定义和其对象的创建同时发生。

匿名类最大的问题就是“代码高度问题”,它和单独实现类一样,具有完整的类体结构,包含很多公式化的代码/样板代码(Boilerplate Code),而程序员通常在意的仅仅是op的方法体,因此匿名类显得占用太多的行数。为了避免笨重而啰嗦的代码,减少代码的行数/压缩代码的高度,Java 8提供了λ(lambda)表达式。忽略Java编译器对λ表达式与匿名类的不同处理,可以认为:λ表达式是(函数接口的)匿名类的语法糖技术上,lambda表达式并没有提供新能力,现在程序员能够做的事情,在Java 8之前完全能够做。

Java8引入的λ表达式对特定的(函数接口的)匿名类进行精简,简化方式如图1-1所示。

精简过程包含:

①删除new BinaryOP()。这就暗示着λ表达式对使用场景有一些要求,必须能够从使用场景/环境/上下文中能够推断λ表达式/匿名类的父类型.因此,一个λ表达式只能够用于能确定其父类型——称为该λ表达式的目标类型(Target typing)的场合,例如赋值语句、作为实参和强制类型转换中,而且目标类型必须是函数接口(可以用@FunctionalInterface来强调这一点)。

★lambda表达式是函数接口的一个具体匿名类(的对象的引用)。匿名类和λ表达式,本质上如同AddOp,都是接口DoubleOP的实现类。

★lambda表达式是(函数接口的)匿名类的语法糖。

②删除op函数的方法头,包括修饰符、返回值类型和函数名,因为BinaryOP的op的这些东西是已知的。找到匿名类的父类型——某个函数接口(Functional interfaces后,之所以能够省略函数名,是因为函数接口具有一个必须的特点:它是SAM(单一抽象方法/Single Abstract Method)形式的接口,它只有唯一的抽象方法,而该抽象方法的名字就是λ表达式所省略的。

为什么要保留形参列表?因为方法体的代码中用到了m和n,需要交代其含义;

③形参列表中变量的数据类型也可以删除,因为op参数的数据类型,是已知的。通常λ表达式不需要写出形参列表中参数的类型,某些时候,显式地写出参数的数据类型可能使程序更具有可读性,程序员可以从可读性上决定是否省略参数的类型;不论省略与否,编译器能够进行类型推断。如果形参列表中只有一个参数时,可以省略形参列表的括号。但是没有参数时,需要表示形参列表的一个空括号。

④对匿名类的函数体也可以简化。如果函数体为单一的语句而且返回一个结果时,函数体简化为return后的表达式。

(有人说:λ表达式->后面,可以是表达式,或一个语句块。当函数体为单一的语句而且返回一个结果时,用表达式作为函数体更简洁。

看见这样的话,我就头疼。)   

// Test 
    public static double use(BinaryOP clazz, int m, int n) {//形参DoubleOP
        return clazz.op(m, n);
    }
    public static void testLambda(){
        //赋值语句
        BinaryOP clazz = (m, n) -> {
            return m + 2 * n;
        };
        clazz = (m, n) -> m + 2 * n; //再精简
        double d = clazz.op(1, 3);        pln(d);
        //λ表达式作为实参
        d = use(clazz , 1,3);pln(d);
        //直接使用λ表达式
        d = ((BinaryOP) (m, n) -> m + 2 * n).op(1, 3);   pln(d);
    }

通过λ表达式对匿名类进行精简的过程,可知λ表达式由三部分组成:(参数列表),英文减号和大于号构成的箭头->,函数体。

λ表达式看起来如同一个函数,lambda这个词源于邱奇的拉姆达运算/lambda calculus。但是,在语法上,Java的λ表达式毕竟不是一个函数。另外需要注意的是,Java的λ表达式并不是没有名字(匿名类没有程序员定义的类名——编译器会给出一个名字,所以不要将Java的λ表达式称为匿名函数)而是省略了名字。

虽然程序员可以简单地认为λ表达式是(函数接口的)匿名类,但是λ表达式和匿名类有两点不同:①编译器并没有将λ表达式简单地转换成匿名类。编译器会为每个匿名类生成一个新的.class文件,而编译器则通过invokedynamic指令,把λ表达式的字节码生成延迟到运行时。②λ表达式与匿名类中使用this的含义不同。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值