1.接口改善
a.接口中可以定义静态方法
b.更重要的是,接口中的方法,可以用default修饰后,添加方法体
2.为什么不能用默认方法来重写equals,hashcode,tostring方法?
即接口不能提供对object类的任何方法的默认实现。如果一个类实现了一个方法,那总是优先于默认的实现的。一旦所有接口的实例都是object的子类,所有接口实例都已经有对equals/hashcode/tostring的非默认实现。因此,一个在接口上这些的默认版本都是没用的,它也不会被编译。
3.函数式接口
核心概念就是函数式接口。如果一个接口定义个唯一一个抽象方法,那么这个接口就成为函数式接口。比如,java.lang.runnable就是一个函数式接口,因为它只定义一个抽象方法:
public abstract void run();
什么是函数式接口,有两种情况:1.接口只有一个抽象方法,abstract修饰2.接口只有一个抽象方法,abstract修饰。同时,包含多个默认方法,因为默认方式是被default修饰,不是被abstract修饰。
同时,引入了一个新的annotation:@functionalinterface。可以把他它放在一个接口前,表示这个接口是一个函数式接口。加上它的接口不会被编译,除非你设法把它变成一个函数式接口。它有点像@override,都是声明了一种使用意图,避免你把它用错。
4.lambdas
一个函数式接口非常有价值的属性就是他们能够用lambdas来实例化。这里有一些lambdas的例子:
左边是指定类型的逗号分割的输入列表,右边是带有return的代码块:
(int x, int y) -> { return x + y; }
左边是推导类型的逗号分割的输入列表,右边是返回值:
(x, y) -> x + y
左边是推导类型的单一参数,右边是一个返回值:
x -> x * x
左边没有输入 (官方名称: "burger arrow"),在右边返回一个值:
() -> x
左边是推导类型的单一参数,右边是没返回值的代码块(返回void):
x -> { system.out.println(x); }
静态方法引用:
string::valueof
非静态方法引用:
object::tostring
继承的函数引用:
x::tostring
构造函数引用:
arraylist::new
你可以想出一些函数引用格式作为其他lambda格式的简写。
方法引用 等价的lambda表达式
string::valueof x -> string.valueof(x)
object::tostring x -> x.tostring()
x::tostring () -> x.tostring()
arraylist::new () -> new arraylist<>()
当然,在java里方法能被重载。类可以有多个同名但不同参数的方法。这同样对构造方法有效。arraylist::new能够指向它的3个构造方法中任何一个。决定使用哪个方法是根据在使用的函数式接口。
一个lambda和给定的函数式接口在“外型”匹配的时候兼容。通过“外型”,我指向输入、输出的类型和声明检查异常。
给出两个具体有效的例子:
comparator<string> c = (a, b) -> integer.compare(a.length(),
b.length());
一个comparator<string>的compare方法需要输入两个阐述,然后返回一个int。这和lambda右侧的一致,因此这个任务是有效的。
runnable r = () -> { system.out.println("running!"); }
一个runnable的run方法不需要参数也不会返回值。这和lambda右侧一致,所以任务有效。
在抽象方法的签名里的受检查异常(如果存在)也很重要。如果函数式接口在它的签名里声明了异常,lambda只能抛出受检查异常。
5.捕获和非捕获的lanbdas表达式
当lambda表达式访问一个定义在lambda表达式体外的非静态变量或者对象时,这个lambda表达式称为“捕获的”。比如,下面这个lambda表达式捕捉了变量x:
int x = 5; return y -> x + y;
为了保证这个lambda表达式声明是正确的,被它捕获的变量必须是“有效final”的。所以要么它们需要用final修饰符号标记,要么保证它们在赋值后不能被改变。
lambda表达式是否是捕获的和性能悄然相关。一个非不捕获的lambda通常比捕获的更高效,虽然这一点没有书面的规范说明(据我所知),而且也不能为了程序的正确性指望它做什么,非捕获的lambda只需要计算一次. 然后每次使用到它都会返回一个唯一的实例。而捕获的lambda表达式每次使用时都需要重新计算一次,而且从目前实现来看,它很像实例化一个匿名内部类的实例。
6.其他
lambdas不做的事
你应该记住,有一些lambdas不提供的特性。为了java 8它们被考虑到了,但是没有被包括进去,由于简化以及时间限制的原因。
non-final* 变量捕获 - 如果一个变量被赋予新的数值,它将不能被用于lambda之中。"final"关键字不是必需的,但变量必须是“有效final”的(前面讨论过)。这个代码不会被编译:
int count = 0;
list<string> strings = arrays.aslist("a", "b", "c");
strings.foreach(s -> {
count++; // error: can't modify the value of count });
例外的透明度 - 如果一个已检测的例外可能从lambda内部抛出,功能性的接口也必须声明已检测例外可以被抛出。这种例外不会散布到其包含的方法。这个代码不会被编译:
void appendall(iterable<string> values, appendable out) throws ioexception { // doesn't help with the error values.foreach(s -> {out.append(s); // error: can't throw ioexception here // consumer.accept(t) doesn't allow it });}
有绕过这个的办法,你能定义自己的功能性接口,扩展consumer的同时通过像runtimeexception之类抛出 ioexception。我试图用代码写出来,但发现它令人困惑是否值得。
控制流程 (break, early return) -在上面的 foreach例子中,传统的继续方式有可能通过在lambda之内放置 "return;"来实现。但是,没有办法中断循环或者从lambda中通过包含方法的结果返回一个数值。例如:
final string secret = "foo"; boolean containssecret(iterable<string> values) {
values.foreach(s -> { if (secret.equals(s)) {
? // want to end the loop and return true, but can't }});
}
进一步阅读关于这些问题的资料,看看这篇brian goetz写的说明:在 block<t>中响应“已验证例外”super0555
其它翻译版本(1)
为什么抽象类不能通过利用lambda实例化抽象类,哪怕只声明了一个抽象方法,也不能使用lambda来实例化。
下面有两个类 ordering 和 cacheloader的例子,都带有一个抽象方法,摘自于guava 库。那岂不是很高兴能够声明它们的实例,像这样使用lambda表达式?
ordering<string> order = (a, b) -> ...;
cacheloader<string, string> loader = (key) -> ...;
这样做引发的最常见的争论就是会增加阅读lambda的难度。以这种方式实例化一段抽象类将导致隐藏代码的执行:抽象类的构造方法。
另一个原因是,它抛出了lambda表达式可能的优化。在未来,它可能是这种情况,lambda表达式都不会计算到对象实例。放任用户用lambda来声明抽象类将妨碍像这样的优化。
此外,有一个简单地解决方法。事实上,上述两个摘自guava 库的实例类已经证明了这种方法。增加工厂方法将lambda转换成实例。
ordering<string> order = ordering.from((a, b) -> ...);
cacheloader<string, string> loader = cacheloader.from((key) -> ...);
要深入阅读,请参看由 brian goetz所做的说明: response to "allow lambdas to implement abstract classes"。
java.util.function包概要:java.util.function
作为comparator 和runnable早期的证明,在jdk中已经定义的接口恰巧作为函数接口而与lambdas表达式兼容。同样方式可以在你自己的代码中定义任何函数接口或第三方库。
但有特定形式的函数接口,且广泛的,通用的,在之前的jd卡中并不存在。大量的接口被添加到新的java.util.function 包中。下面是其中的一些:
function<t, r> -t作为输入,返回的r作为输出
predicate<t> -t作为输入,返回的boolean值作为输出
consumer<t> - t作为输入,执行某种动作但没有返回值
supplier<t> - 没有任何输入,返回t
binaryoperator<t> -两个t作为输入,返回一个t作为输出,对于“reduce”操作很有用
这些最原始的特征同样存在。他们以int,long和double的方式提供。例如:
intconsumer -以int作为输入,执行某种动作,没有返回值
这里存在性能上的一些原因,主要释在输入或输出的时候避免装箱和拆箱操作。
你应该记住,有一些lambdas不提供的特性。为了java 8它们被考虑到了,但是没有被包括进去,由于简化以及时间限制的原因。
non-final* 变量捕获 - 如果一个变量被赋予新的数值,它将不能被用于lambda之中。"final"关键字不是必需的,但变量必须是“有效final”的(前面讨论过)。这个代码不会被编译:
int count = 0;
list<string> strings = arrays.aslist("a", "b", "c");
strings.foreach(s -> {
count++; // error: can't modify the value of count });
例外的透明度 - 如果一个已检测的例外可能从lambda内部抛出,功能性的接口也必须声明已检测例外可以被抛出。这种例外不会散布到其包含的方法。这个代码不会被编译:
void appendall(iterable<string> values, appendable out) throws ioexception { // doesn't help with the error values.foreach(s -> {out.append(s); // error: can't throw ioexception here // consumer.accept(t) doesn't allow it });}
有绕过这个的办法,你能定义自己的功能性接口,扩展consumer的同时通过像runtimeexception之类抛出 ioexception。我试图用代码写出来,但发现它令人困惑是否值得。
控制流程 (break, early return) -在上面的 foreach例子中,传统的继续方式有可能通过在lambda之内放置 "return;"来实现。但是,没有办法中断循环或者从lambda中通过包含方法的结果返回一个数值。例如:
final string secret = "foo"; boolean containssecret(iterable<string> values) {
values.foreach(s -> { if (secret.equals(s)) {
? // want to end the loop and return true, but can't }});
}
进一步阅读关于这些问题的资料,看看这篇brian goetz写的说明:在 block<t>中响应“已验证例外”
super0555
翻译于 4年前
4人顶
顶 翻译得不错哦!
其它翻译版本(1)
为什么抽象类不能通过利用lambda实例化抽象类,哪怕只声明了一个抽象方法,也不能使用lambda来实例化。
下面有两个类 ordering 和 cacheloader的例子,都带有一个抽象方法,摘自于guava 库。那岂不是很高兴能够声明它们的实例,像这样使用lambda表达式?
ordering<string> order = (a, b) -> ...;
cacheloader<string, string> loader = (key) -> ...;
这样做引发的最常见的争论就是会增加阅读lambda的难度。以这种方式实例化一段抽象类将导致隐藏代码的执行:抽象类的构造方法。
另一个原因是,它抛出了lambda表达式可能的优化。在未来,它可能是这种情况,lambda表达式都不会计算到对象实例。放任用户用lambda来声明抽象类将妨碍像这样的优化。
此外,有一个简单地解决方法。事实上,上述两个摘自guava 库的实例类已经证明了这种方法。增加工厂方法将lambda转换成实例。
ordering<string> order = ordering.from((a, b) -> ...);
cacheloader<string, string> loader = cacheloader.from((key) -> ...);
要深入阅读,请参看由 brian goetz所做的说明: response to allow lambdas to implement abstract classes。
等pm
翻译于 4年前
2人顶
顶 翻译得不错哦!
java.util.function包概要:java.util.function
作为comparator 和runnable早期的证明,在jdk中已经定义的接口恰巧作为函数接口而与lambdas表达式兼容。同样方式可以在你自己的代码中定义任何函数接口或第三方库。
但有特定形式的函数接口,且广泛的,通用的,在之前的jd卡中并不存在。大量的接口被添加到新的java.util.function 包中。下面是其中的一些:
function469df1b70b2914a3841e0404ec3e3d5d -t作为输入,返回的r作为输出
predicate8742468051c85b06f0a0af9e3e506b5c -t作为输入,返回的boolean值作为输出
consumer8742468051c85b06f0a0af9e3e506b5c - t作为输入,执行某种动作但没有返回值
supplier8742468051c85b06f0a0af9e3e506b5c - 没有任何输入,返回t
binaryoperator8742468051c85b06f0a0af9e3e506b5c -两个t作为输入,返回一个t作为输出,对于“reduce”操作很有用
这些最原始的特征同样存在。他们以int,long和double的方式提供。例如:
intconsumer -以int作为输入,执行某种动作,没有返回值
这里存在性能上的一些原因,主要释在输入或输出的时候避免装箱和拆箱操作。
等pm
翻译于 4年前
2人顶
顶 翻译得不错哦!
1
2
3
>
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 cc 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
评论(85)
以上就是java8有哪些新的特性的详细内容。
