Java 8新特性

       Java 8可谓是自Java 5以来最具革命性的版本了,她在语言、编译器、类库、开发工具以及Java虚拟机等方面都带来了不少新特性。我们来一一回顾十大特性。

 

一、Lambdas表达式与Functional接口

       Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中),或者把代码看成数据:函数式程序员对这一概念非常熟悉。在最简单的形式中,一个lambda可以由用逗号分隔的参数列表、–>符号与函数体三部分表示。例如:

Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );

请注意参数e的类型是由编译器推测出来的。同时,你也可以通过把参数类型与参数包括在括号中的形式直接给出参数的类型:

Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );

在某些情况下lambda的函数体会更加复杂,这时可以把函数体放到在一对花括号中,就像在Java中定义普通函数一样。例如:

Arrays.asList( "a", "b", "d" ).forEach( e -> {
    System.out.print( e );
    System.out.print( e );
} );

Lambda可以引用类的成员变量与局部变量(如果这些变量不是final的话,它们会被隐含的转为final,这样效率更高)。例如,下面两个代码片段是等价的:

String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach( 
    ( String e ) -> System.out.print( e + separator ) );
final String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach( 
    ( String e ) -> System.out.print( e + separator ) );

Lambda可能会返回一个值。返回值的类型也是由编译器推测出来的。如果lambda的函数体只有一行的话,那么没有必要显式使用return语句。下面两个代码片段是等价的:

Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
    int result = e1.compareTo( e2 );
    return result;
} );

问:如何使现有的函数友好地支持lambda?答:增加函数式接口的概念!

函数式接口:一个具有一个方法的普通接口。像这样的接口,可以被隐式转换为lambda表达式。java.lang.Runnable(只有run())与java.util.concurrent.Callable(只有call())是函数式接口最典型的两个例子。在实际使用过程中,函数式接口是容易出错的:如有某个人在接口定义中增加了另一个方法,这时,这个接口就不再是函数式的了,并且编译过程也会失败。为了克服函数式接口的这种脆弱性并且能够明确声明接口作为函数式接口的意图,Java 8增加了一种特殊的注解@FunctionalInterface(Java 8中所有类库的已有接口都添加了@FunctionalInterface注解)。让我们看一下这种函数式接口的定义:

@FunctionalInterface
public interface Functional {
    void method();
}

默认方法和静态方法并不影响函数式接口的契约,可以任意使用;但是接口一旦注解了@FunctionalInterface,就只能有一个非默认非静态的方法,否则编译会报错。​​​​​​​

@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();
         
    //默认方法
    default void defaultMethod() {  
          
    }      
    
    //静态方法
    static void staticMethod(){

    }  

}

Lambda是Java 8最大的卖点。它能够在纯Java语言环境中提供一种优雅的方式来支持函数式编程。


二、接口的默认与静态方法

Java8用默认方法与静态方法这两个新概念来扩展接口的声明。接口中的“普通”(非默认非静态)方法是抽象方法。默认方法与抽象方法不同之处在于类实现接口时必须实现(Override)抽象方法,但是默认方法则没有这个要求(实现与否都可以)。但是默认方法必须提供一个所谓的默认实现,这样所有的接口实现者将会默认继承它(如果有必要,可以覆盖这个默认实现)。让我们看看下面的例子:

private interface Defaulable {
    // Interfaces now allow default methods, the implementer may or 
    // may not implement (override) them.
    default String notRequired() { 
        return "Default implementation"; 
    }        
}
         
private static class DefaultableImpl implements Defaulable {
}
     
private static class OverridableImpl implements Defaulable {
    @Override
    public String notRequired() {
        return "Overridden implementation";
    }
}

Java 8带来的另一个有趣的特性是接口可以声明(并且可以提供实现)静态方法。例如:

private interface DefaulableFactory {
    //形参为Defaultable类型的Supplier容器,此时并不会调用对象的构造方法,即不会创建对象
    static Defaulable create( Supplier< Defaulable > supplier ) {
        //调用get()方法,此时会调用对象的构造方法,即获得到真正对象
        return supplier.get();
    }
}

下面的一小段代码片段把上面的默认方法与静态方法黏合到一起。

public static void main( String[] args ) {
    //DefaultableImpl::new
    //创建Supplier容器,声明为DefaultableImpl类型,此时并不会调用对象的构造方法,即不会创建对象  
    //通过create()获取真正的对象
    Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
    System.out.println( defaulable.notRequired() );
         
    defaulable = DefaulableFactory.create( OverridableImpl::new );
    System.out.println( defaulable.notRequired() );
}

这个程序的控制台输出如下:

Default implementation
Overridden implementation

在JVM中,默认方法的实现是非常高效的,并且通过字节码指令为方法调用提供了支持。默认方法允许继续使用现有的Java接口,而同时能够保障正常的编译过程。这方面好的例子是大量的方法被添加到java.util.Collection接口中去:stream(),parallelStream(),forEach(),removeIf(),……


三、方法引用

方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

下面,我们以定义了4个方法的Car这个类作为例子,区分Java中支持的4种不同的方法引用。

public static class Car {
    public static Car create( final Supplier< Car > supplier ) {
        return supplier.get();
    }              
         
    public static void collide( final Car car ) {
        System.out.println( "Collided " + car.toString() );
    }
         
    public void follow( final Car another ) {
        System.out.println( "Following the " + another.toString() );
    }
         
    public void repair() {   
        System.out.println( "Repaired " + this.toString() );
    }
}

第一种方法引用是构造器引用,它的语法是Class::new,或者更一般的Class< T >::new。请注意构造器没有参数。

final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );

第二种方法引用是静态方法引用,它的语法是Class::static_method。请注意这个方法接受一个Car类型的参数。

cars.forEach( Car::collide );

第三种方法引用是特定类的任意对象的方法引用,它的语法是Class::method。请注意,这个方法没有参数。

cars.forEach( Car::repair );

第四种方法引用是特定对象的方法引用,它的语法是instance::method。请注意,这个方法接受一个Car类型的参数

final Car police = Car.create( Car::new );
cars.forEach( police::follow );

四、重复注解

在Java 5中使用注解有一个限制,即相同的注解在同一位置只能声明一次。Java 8引入重复注解,这样相同的注解在同一地方也可以声明多次。重复注解机制本身需要用@Repeatable注解。Java 8在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化。

五、扩展注解的支持

Java 8扩展了注解的上下文,几乎可以为任何东西添加注解,包括局部变量、泛型类、父类与接口的实现,连方法的异常也能添加注解。

六、Optional

Java 8引入Optional类来防止空指针异常,Optional类最先是由Google的Guava项目引入的。Optional类实际上是个容器:它可以保存类型T的值,或者保存null。使用Optional类我们就不用显式进行空指针检查了。

七、Stream

Stream API是把真正的函数式编程风格引入到Java中。其实简单来说可以把Stream理解为MapReduce,当然Google的MapReduce的灵感也是来自函数式编程。她其实是一连串支持连续、并行聚集操作的元素。从语法上看,也很像linux的管道、或者链式编程,代码写起来简洁明了,非常酷帅!

八、Date/Time API (JSR 310)

Java 8新的Date-Time API (JSR 310)受Joda-Time的影响,提供了新的java.time包,可以用来替代 java.util.Date和java.util.Calendar。一般会用到Clock、LocaleDate、LocalTime、LocaleDateTime、ZonedDateTime、Duration这些类,对于时间日期的改进还是非常不错的。

九、JavaScript引擎Nashorn

Nashorn允许在JVM上开发运行JavaScript应用,允许Java与JavaScript相互调用。

十、Base64

在Java 8中,Base64编码成为了Java类库的标准。Base64类同时还提供了对URL、MIME友好的编码器与解码器。

 

原文链接: javacodegeeks 翻译: ImportNew.com 刘 家财
译文链接: http://www.importnew.com/11908.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值