本帖最后由 tuan2016 于 2016-5-15 00:14 编辑
2.2 接口的默认方法与静态方法Java 8用默认方法与静态方法这两个新概念来扩展接口的声明。默认方法使接口有点像Traits(Scala中特征(trait)类似于Java中的Interface,但它可以包含实现代码,也就是目前Java8新增的功能),但与传统的接口又有些不一样,它允许在已有的接口中添加新方法,而同时又保持了与旧版本代码的兼容性。 默认方法与抽象方法不同之处在于抽象方法必须要求实现,但是默认方法则没有这个要求。相反,每个接口都必须提供一个所谓的默认实现,这样所有的接口实现者将会默认继承它(如果有必要的话,可以覆盖这个默认实现)。让我们看看下面的例子: - 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";
- }
- }
复制代码
Defaulable接口用关键字default声明了一个默认方法notRequired(),Defaulable接口的实现者之一DefaultableImpl实现了这个接口,并且让默认方法保持原样。Defaulable接口的另一个实现者OverridableImpl用自己的方法覆盖了默认方法。 Java 8带来的另一个有趣的特性是接口可以声明(并且可以提供实现)静态方法。例如: - private interface DefaulableFactory {
- // Interfaces now allow static methods
- static Defaulable create( Supplier< Defaulable > supplier ) {
- return supplier.get();
- }
- }
复制代码
下面的一小段代码片段把上面的默认方法与静态方法黏合到一起。 - public static void main( String[] args ) {
- 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(),…… 尽管默认方法非常强大,但是在使用默认方法时我们需要小心注意一个地方:在声明一个默认方法前,请仔细思考是不是真的有必要使用默认方法,因为默认方法会带给程序歧义,并且在复杂的继承体系中容易产生编译错误。更多详情请参考官方文档
2.3 方法引用方法引用提供了非常有用的语法,可以直接引用已有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程序在控制台上会有下面的输出(Car的实例可能不一样): - Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
- Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
- Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
复制代码
关于方法引用的更多详情请参考官方文档。
2.4 重复注解自从Java 5引入了注解机制,这一特性就变得非常流行并且广为使用。然而,使用注解的一个限制是相同的注解在同一位置只能声明一次,不能声明多次。Java 8打破了这条规则,引入了重复注解机制,这样相同的注解可以在同一地方声明多次。 重复注解机制本身必须用@Repeatable注解。事实上,这并不是语言层面上的改变,更多的是编译器的技巧,底层的原理保持不变。让我们看一个快速入门的例子: - package com.javacodegeeks.java8.repeatable.annotations;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Repeatable;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- public class RepeatingAnnotations {
- @Target( ElementType.TYPE )
- @Retention( RetentionPolicy.RUNTIME )
- public @interface Filters {
- Filter[] value();
- }
-
- @Target( ElementType.TYPE )
- @Retention( RetentionPolicy.RUNTIME )
- @Repeatable( Filters.class )
- public @interface Filter {
- String value();
- };
-
- @Filter( "filter1" )
- @Filter( "filter2" )
- public interface Filterable {
- }
-
- public static void main(String[] args) {
- for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
- System.out.println( filter.value() );
- }
- }
- }
复制代码
正如我们看到的,这里有个使用@Repeatable( Filters.class )注解的注解类Filter,Filters仅仅是Filter注解的数组,但Java编译器并不想让程序员意识到Filters的存在。这样,接口Filterable就拥有了两次Filter(并没有提到Filter)注解。 同时,反射相关的API提供了新的函数getAnnotationsByType()来返回重复注解的类型(请注意Filterable.class.getAnnotation( Filters.class )经编译器处理后将会返回Filters的实例)。 程序输出结果如下:
更多详情请参考官方文档
|