浏览 2152 次
锁定老帖子 主题:Java之Lambda的初步学习
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-02-13
说道Lambda项目,不得不先了解下“Lambda”的含义(数学知识丰富的可以略过)。Lambda表示数学符号“λ”,λ演算是计算机科学的一个基础,λ算法是被设计出来研究函数的定义、应用和递归的。表达了计算机中最基本的概念:“调用”和“置换”。基于λ演算发展出了不少新的演算方法,近年来很多经典的并发程序模型都是以它们为基础的。简而言之就是相当的牛X。 回到Lambda项目里,Java引入Lambda语法的目的是为了加入Java对闭包的更好(完全?)支持。 先来看看现有语言中的Lambda语法,比如Python。 在Python中,定义一个方法:
def fun(x): return x*2 运行 fun(5) 输出 10
如果使用lambda语法则为:
(lambda x: x*2)(5) 输出10. 可以看到出,这样可以省略“定义”方法的步骤,或者说更灵活的定义一个方法。
同样,Scheme、Groovy、JavaScript、Ruby等众多动态语言中都可以将函数作为对象,即些函数可以存储到变量中、作为参数传递给其他函数,这意味这函数可以被“动态”的创建和返回。C语言中也有函数指针,不过需要预先定义好参数列表。
回到Java中,lambda语法的最直接的使用方法如下:
class A{ private static int method(int x,int y){ return x+y; } } public static void main(String[] args){ System.out.println(A.method(2,3)); } 运行结果为5 换成Lambda语法变成:
public static void main(String[] args){ Func f = #(int x int y)(x + n); System.out.println(f.(2,3));//这个.用真不舒服…… } 运行结果为5. 这里#(int x int y)(x + n) 表示了 A.method()方法。
Lambda语法 为 #()()
第一个'#'表示为Lambda语法。
第一个()中标记出的是参数的列表。
第二个()是函数体,如果函数体只有单条语句,则可以用(),且不需要return关键字。如果有多条语句,必须用{},且有关键字,比如#(int x, int y){ x+=1 ; y+=2; return x+y;}//这里只是举例子说明,能简化当然就简化了。这里吐槽一下,java的Lambda语法很不“优雅”。
如果仅从使用和实用角度来看Lambda语法似乎意义并不大,并且目前并不优雅的语法已经引起了不少非议,不过oracle大力推行Lambda语法的目的是从闭包角度来说的。
说道闭包又是个讨论很多的话题,数学上闭包的定义很明确:对于映射F,集合S的任意成员x,都满足F(X)属于S,则称集合S在映射F下是满足闭包性质的。到了编程的领域,闭包(Closure)是词法闭包(Lexical Closure)的简称,对于它的定义有不同的观点,这里就不深究了。满足闭包的程序语言一般具有的性质是:
1.函数是一阶值(First-class value),即上文提到的,函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值。
2.函数可以嵌套定义,即在一个函数内部可以定义另一个函数。
更多关于Java闭包的探讨可以看看这里:http://www.ibm.com/developerworks/cn/java/j-jtp04247.html
Lambda语法的实现(这里是参考别人之前的文章,并没实际实验过,仅供参考)。
java语言支持闭包的传统手段是匿名内部类,在jdk1.6中引入了注解处理器,他可以提供新的语言特性,即单一抽象方法(Single Abstract Method),简称SAM类型。而Lambda通过invokedynamic指令转换成SAM类型。
我主要参考的是Rémi Forax的博客,他写的最清楚,地址为http://weblogs.java.net/blog/forax/archive/2011/01/04/jsr-292-goodness-lambda-sam-type-conversion-using-invokedynamic。
目前Java没有生成invokedynamic的语法(Rémi Forax在写博客时),所以在博客中使用的是伪Java代码,和将来的实际代码会有所区别。
比如有Lambda语法:
Comparator<Object, Object> c = #{ o1, o2 -> 1 }; 编译器的步骤为:
1.为这个lambda计划生成一个静态方法 lambda$1。
2.生成这个静态方法对应的方法句柄(MethodHandle)。
3.将这个方法句柄翻译进一个class实例子,这个class实现 java.lang.reflect.Proxy接口。到这发现又是“代理”了。
此时,即创建实现java.lang.reflect.Proxy接口的class实例时,lumbda还未绑定任何局部变量,
这时被编译为:
Comparator c = (Comparator) invokedynamic [Lambdas#asSamBSM, #lambda$1, Comparator.class] ();
在未来计划版本中, ASM将支持invokedynamic的boostrap参数是:
mv.visitIndyMethodInsn("_", "()Ljava/util/Comparator;", new MHandle(MHandle.REF_invokeStatic, "Lambdas", "asSamBSM", "(Ljava/dyn/MethodHandles$Lookup;Ljava/lang/String;Ljava/dyn/MethodType;[Ljava/lang/Object;)Ljava/dyn/CallSite;"), new Object[] { new MHandle(MHandle.REF_invokeStatic, "LambdaTest", "lambda$1", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I"), Type.getObjectType("java/util/Comparator") }); mv.visitVarInsn(ASTORE, 1);
invokedynamic的bootstrap方法代码是
public class Lambdas { public static CallSite asSamBSM(Lookup lookup, String name, MethodType methodType, Object[] bsmArgs) { MethodHandle lambda = (MethodHandle)bsmArgs[0]; Class<?> samInterface = (Class<?>)bsmArgs[1]; if (methodType.returnType() != samInterface) { throw new InvokeDynamicBootstrapError("asSam incompatible return type "+methodType); } int parameterCount = methodType.parameterCount(); if (parameterCount == 0) { // constant case Object samInstance = MethodHandles.asInstance(lambda, samInterface); return new ConstantCallSite( MethodHandles.constant(samInterface, samInstance)); } if (parameterCount == 1) { // bind case MethodHandle combiner = BIND_TO.bindTo(lambda); MethodHandle target = MethodHandles.insertArguments(AS_INSTANCE, 1, samInterface); target = MethodHandles.dropArguments(target, 1, methodType.parameterType(0)); return new ConstantCallSite( MethodHandles.foldArguments(target, combiner).asType(methodType)); } throw new InvokeDynamicBootstrapError("asSam incompatible method type "+methodType); } private static final MethodHandle AS_INSTANCE; private static final MethodHandle BIND_TO; static { try { Lookup lookup = MethodHandles.publicLookup(); AS_INSTANCE = lookup.findStatic(MethodHandles.class, "asInstance", MethodType.methodType(Object.class, MethodHandle.class, Class.class)); BIND_TO = lookup.findVirtual(MethodHandle.class, "bindTo", MethodType.methodType(MethodHandle.class, Object.class)); } catch (NoAccessException e) { throw new LinkageError("linkage error", e); } } }
无论lumbda的语法怎样它都已经来,基于lumbda特性,JVM或许能更好的支持脚本语言。这两天会抽时间多多研究。希望大家拍砖! 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |