java 8新特点归纳(上集)

    作者:课课家教育更新于: 2016-01-08 10:55:42

    大神带你学编程,欢迎选课

      这篇文章是对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 action) {

      Objects.requireNonNull(action); for (T t : this) {

      action.accept(t);

      在过去,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

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

      ?1x -> x * x

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

      ?1() -> x

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

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

      静态方法引用:

      1String::valueOf

      非静态方法引用:

      1Object::toString

      继承的函数引用:

      1x::toString

      构造函数引用:

      1ArrayList::new

      你可以想出一些函数引用格式作为其他lambda格式的简写。 

         excepiton

         翻译于 3年前

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

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

      2Comparator c = (a, b) -> Integer.compare(a.length(),

      b.length());

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

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

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

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

课课家教育

未登录

1