设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客

新版观察:Java 8的新特性和改进总览

2013-5-2 10:12| 发布者: joejoe0332| 查看: 4480| 评论: 0|原作者: super0555, throwable, 等PM, LinuxQueen, JeromeCui, 乔学士, Sender, 叫我蝴蝶吧|来自: oschina

摘要:   这篇文章是对Java 8中即将到来的改进做一个面向开发者的综合性的总结,JDK的这一特性将会在2013年9月份发布。   在写这篇文章的时候,Java 8的开发工作仍然在紧张有序的进行中,语言特新和API仍然有可能改变 ...

  这篇文章是对Java 8中即将到来的改进做一个面向开发者的综合性的总结,JDK的这一特性将会在2013年9月份发布。

  在写这篇文章的时候,Java 8的开发工作仍然在紧张有序的进行中,语言特新和API仍然有可能改变,我会尽我最大的努力保持这份文档跟得到Java 8的改动。

  Java 8的预览版,也就是 “Project Lambda”,现在可以从java.net下载到。

  我使用了IntelliJ的预览版做我的IDE,在我看来他是目前支持java 8特性最好的一个IDE,你可以从这里下载到.

  由于我没有找到Oracle发布的Java 8的官方文档,所以目前Java 8的文档还只有本地版本,等Oracle公开文档的时候,我将会重新链接到官方文档。

  接口改善

  现在接口里已经完全可以定义静态方法了. 举一个比较普遍的例子就是在java类库中, 对于一些接口如Foo, 都会有一个有静态方法的工具类Foos 来生成或者配合Foo对象实例来使用. 既然静态方法可以存在于接口当中, 那么大多数情况下 Foos工具类完全可以使用接口中的公共方法来代理 (或者将Foos置成package-private).

  除此之外更重要的就是, Java 8中接口可以定义默认的方法了.举个例子,一个for-each循环的方法就可以加入到java.lang.Iterable中:

1public default void forEach(Consumer<? super T> action) {
2    Objects.requireNonNull(action); for (T t : this) {
3        action.accept(t);
4    }
5}

  在过去,java类库的接口中添加方法基本上是不可能的. 在接口中添加方法意味着破坏了实现了这个接口的代码. 但是现在, 只要能够提供一个正确明智的默认的方法的实现, java类库的维护者就可以在接口中添加方法.

  Java 8中, 大量的默认方法已经被添加到核心的JDK接口中了. 稍候我会详细介绍它们.

  为什么不能用默认方法来重载equals,hashCode和toString?

  接口不能提供对Object类的任何方法的默认实现。特别是,这意味着从接口里不能提供对equals,hashCode或toString的默认实现。

  这刚看起来挺奇怪的,但考虑到一些接口实际上是在文档里定义他们的equals行为的。List接口就是一个例子了。因此,为什么不允许这样呢?

  Brian Goetz在这个问题上的冗长的回复里给出了4个原因。我这里只说其中一个,因为那个已经足够说服我了:

  它会变得更困难来推导什么时候该调用默认的方法。现在它变得很简单了:如果一个类实现了一个方法,那总是优先于默认的实现的。一旦所有接口的实例都是 Object的子类,所有接口实例都已经有对equals/hashCode/toString的非默认实现。因此,一个在接口上这些的默认版本都是没用 的,它也不会被编译。

  要看更多的话,看下由Brian Goetz写的解释: 对“允许默认方法来重载Object的方法”的回复

  函数式接口

  Java 8 引入的一个核心概念是函数式接口。如果一个接口定义个唯一一个抽象方法,那么这个接口就成为函数式接口。比如,java.lang.Runnable就是一个函数式接口,因为它只顶一个一个抽象方法:

1public abstract void run();
  留意到“abstract”修饰词在这里是隐含的,因为这个方法缺少方法体。为了表示一个函数式接口,并非想这段代码一样一定需要“abstract”关键字。

  默认方法不是abstract的,所以一个函数式接口里可以定义任意多的默认方法,这取决于你。

  同时,引入了一个新的Annotation:@FunctionalInterface。可以把他它放在一个接口前,表示这个接口是一个函数式接口。加上它的接口不会被编译,除非你设法把它变成一个函数式接口。它有点像@Override,都是声明了一种使用意图,避免你把它用错。

Lambdas

一个函数式接口非常有价值的属性就是他们能够用lambdas来实例化。这里有一些lambdas的例子:

左边是指定类型的逗号分割的输入列表,右边是带有return的代码块:

1(int x, int y) -> { return x + y; }

左边是推导类型的逗号分割的输入列表,右边是返回值:

1(x, y) -> x + y

左边是推导类型的单一参数,右边是一个返回值:

左边没有输入 (官方名称: "burger arrow"),在右边返回一个值:

左边是推导类型的单一参数,右边是没返回值的代码块(返回void):

1x -> { System.out.println(x); }

静态方法引用:

1String::valueOf

非静态方法引用:

1Object::toString

继承的函数引用:

1x::toString

构造函数引用:

1ArrayList::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和给定的函数式接口在“外型”匹配的时候兼容。通过“外型”,我指向输入、输出的类型和声明检查异常。

给出两个具体有效的例子:

1Comparator<String> c = (a, b) -> Integer.compare(a.length(),
2                                                 b.length());

一个Comparator<String>的compare方法需要输入两个阐述,然后返回一个int。这和lambda右侧的一致,因此这个任务是有效的。

1Runnable r = () -> { System.out.println("Running!"); }

一个Runnable的run方法不需要参数也不会返回值。这和lambda右侧一致,所以任务有效。

在抽象方法的签名里的受检查异常(如果存在)也很重要。如果函数式接口在它的签名里声明了异常,lambda只能抛出受检查异常。


酷毙

雷人

鲜花
1

鸡蛋

漂亮

刚表态过的朋友 (1 人)

  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部