ie8启用java
技术行业的一种不诚实的看法是,开发人员从来没有比没有免费啤酒或有机会抱怨所提供的东西感到高兴的人。
因此,尽管马克·雷因霍尔德(Mark Reinhold)和Java团队做出了努力,在Oracle收购Oracle之后(计划A /计划B的决定)使社区参与路线图,但许多Java开发人员仍认为Java 7并不是“发行版太多”。
在本文中,我将通过探究Java 7中的功能为Java 8中的新功能奠定基础,来反驳这一论点。
钻石算子
Java经常因过于冗长而受到批评。 表示投诉的最常见领域之一是任务分配。 在Java 6中,我们被迫编写如下的赋值语句:
Map m =新的HashMap ();
该语句包含许多冗余信息-我们应该能够以某种方式让编译器自己找出更多信息,而不要求程序员这么明确。
实际上,诸如Scala之类的语言会根据表达式进行大量的类型推断,实际上赋值语句可以这样编写:
val m = Map(“ x”-> 24,“ y”-> 25,“ z”-> 26);
关键字val表示此变量可能不会被重新分配给它(就像Java变量的关键字final一样)。 根本没有指定有关变量的类型信息-而是Scala编译器检查赋值的右侧,并通过查看分配的值来确定变量的正确类型。
在Java 7中,引入了一些有限的类型推断功能,现在可以像下面这样编写赋值语句:
Map m =新的HashMap <>();
此形式与Scala形式之间的主要区别在于,在Scala中,值具有显式类型,并且可以推断出变量的类型。 在Java 7中,变量的类型是显式的,关于值的类型信息就是所推断的。
一些开发人员抱怨他们会首选Scala解决方案,但事实证明,在Java 8的主要功能-lambda表达式的上下文中,它不那么方便。
在Java 8中,我们可以编写一个将2加到整数的函数,如下所示:
函数fn = x-> x + 2;
接口Function是Java 8中的新增功能-它位于程序包java.util.function中,并且包含原始类型的专用格式。 但是,我们选择了这种语法,因为它与Scala等效语言非常相似,并使开发人员可以更轻松地看到相似之处。
通过将fn的类型显式指定为带有一个Integer参数并返回另一个Integer的Function,则Java编译器能够推断参数x-Integer的类型。 这与我们在Java 7 Diamond语法中看到的模式相同-我们指定变量的类型,然后推断值的类型。
让我们看一下对应的Scala lambda表达式:
val fn =(x:Int)=> x + 2;
在这里,我们必须明确指定参数x的类型,因为我们没有fn的精确类型,因此我们无从推断。 Scala格式并不是很难读懂的,但是Java 8格式具有一定的语法清洁度,可以直接追溯到Java 7的菱形语法。
方法句柄
同时,方法句柄是Java 7的最重要的新功能,也是大多数Java开发人员日常生活中最不可能使用的功能。
方法句柄是对要执行的方法的类型化引用。 可以将它们视为“类型安全函数指针”(对于熟悉C / C ++的开发人员)或“为现代Java开发人员重新构想的核心反射”。
方法句柄在lambda表达式的实现中起着很大的作用。 Java 8的早期原型在编译时将每个lambda表达式转换为一个匿名内部类。
最新的beta更加复杂。 让我们首先回顾一下,lambda表达式(至少在Java中是)包含函数签名(在方法中处理API的方法将由MethodType对象表示)和主体,但不一定是函数名称。
这表明我们可以将lambda表达式转换为具有正确签名并包含lambda主体的合成方法。 例如,我们的示例:
函数fn = x-> x + 2;
由Java 8编译器使用以下字节码将其转换为私有方法:
private static java.lang.Integer lambda$0(java.lang.Integer);
descriptor: (Ljava/lang/Integer;)Ljava/lang/Integer;
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokevirtual #13 // Method java/lang/Integer.intValue:()I
4: iconst_2
5: iadd
6: invokestatic #8 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
9: areturn
它具有正确的签名(接受一个Integer,然后返回另一个)和语义。 要使用此lambda表达式,我们将使用一个引用它的方法句柄,并使用它来构建适当类型的对象,这将在我们讨论的下一个功能中看到。
动态调用
Java 7的最终功能为Java 8敞开了大门,比方法句柄更加神秘。 这是新的字节码invokedynamic-自Java 1.0以来第一个要添加到平台的新字节码。 Java开发人员在版本7中几乎不可能使用此功能,因为在任何情况下,版本7 javac都不会发出包含该功能的类文件。
取而代之的是,该字节码是供非Java语言的开发人员使用的,例如JRuby,它比Java需要更多的动态调度。 为了了解invokedynamic的工作原理,让我们讨论Java的方法调用如何编译为字节码。
一个标准的Java方法调用将变成一段JVM字节码,通常称为调用站点。 它包括一个调度操作码(例如,对于常规实例方法调用,为invokevirtual)和一个常量(该类的常量池中的偏移量),该常量指示要调用的方法。
不同的调度操作码具有不同的规则来控制方法查找的完成方式,但是在Java 7之前,常数始终是要调用哪个方法的直接指示。
invokedynamic是不同的。 invokedynamic并没有提供直接指示要调用哪个方法的常量,而是提供了一种间接机制,该机制使用户代码可以在运行时确定要调用的方法。
首次遇到invokedynamic网站时,它还没有已知的目标。 而是调用方法句柄(称为引导方法)。 该引导方法返回一个CallSite对象,该对象包含另一个方法句柄,这是invokedynamic调用的实际目标。
1)执行流中遇到的invokedynamic站点(最初未链接)2)调用引导程序方法并返回一个CallSite对象3)CallSite对象包含一个方法句柄(目标)4)调用目标方法句柄
引导程序方法是用户代码选择需要调用的方法的方式。 对于lambda表达式,平台使用库提供的引导程序方法,称为lambda元工厂。
它具有静态参数,其中包含合成方法的方法句柄(请参见上一节),以及lambda的正确签名。
元工厂返回一个CallSite,其中包含一个方法句柄,该方法句柄将依次返回一个lambda表达式已转换为正确类型的实例。 所以这样的语句:
函数fn = x-> x + 2;
转换为像这样的invokedynamic调用:
Code:
stack=4, locals=2, args_size=1
0: invokedynamic #2, 0 // InvokeDynamic #0:apply:()Ljava/util/function/Function;
5: astore_1
invokedynamic引导程序方法是静态方法LambdaMetafactory.metafactory(),该方法返回链接到目标方法句柄的CallSite对象,该对象将返回实现Function接口的对象。
当invokedynamic指令完成时,实现Function并具有lambda表达式作为其apply()方法的内容的对象位于堆栈的顶部,其余代码可以正常进行。
结论
将lambda表达式放入Java平台始终是一项具有挑战性的工作,但是通过确保适当的基础,Java 7大大减轻了这一工作。 计划B不仅为开发人员提供了Java 7的早期版本,而且还允许对其进行核心技术的全面测试,然后再将其用于Java 8,尤其是在lambda表达式中。
翻译自: https://www.infoq.com/articles/Java-7-Features-Which-Enable-Java-8/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1
ie8启用java