A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© tuan2016 中级黑马   /  2016-5-14 23:10  /  690 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 tuan2016 于 2016-5-15 00:14 编辑

2.2 接口的默认方法与静态方法
Java 8用默认方法与静态方法这两个新概念来扩展接口的声明。默认方法使接口有点像Traits(Scala中特征(trait)类似于Java中的Interface,但它可以包含实现代码,也就是目前Java8新增的功能),但与传统的接口又有些不一样,它允许在已有的接口中添加新方法,而同时又保持了与旧版本代码的兼容性。
默认方法与抽象方法不同之处在于抽象方法必须要求实现,但是默认方法则没有这个要求。相反,每个接口都必须提供一个所谓的默认实现,这样所有的接口实现者将会默认继承它(如果有必要的话,可以覆盖这个默认实现)。让我们看看下面的例子:
  1. private interface Defaulable {
  2.     // Interfaces now allow default methods, the implementer may or
  3.     // may not implement (override) them.
  4.     default String notRequired() {
  5.         return "Default implementation";
  6.     }        
  7. }
  8.         
  9. private static class DefaultableImpl implements Defaulable {
  10. }
  11.    
  12. private static class OverridableImpl implements Defaulable {
  13.     @Override
  14.     public String notRequired() {
  15.         return "Overridden implementation";
  16.     }
  17. }
复制代码

Defaulable接口用关键字default声明了一个默认方法notRequired(),Defaulable接口的实现者之一DefaultableImpl实现了这个接口,并且让默认方法保持原样。Defaulable接口的另一个实现者OverridableImpl用自己的方法覆盖了默认方法。
Java 8带来的另一个有趣的特性是接口可以声明(并且可以提供实现)静态方法。例如:
  1. private interface DefaulableFactory {
  2.     // Interfaces now allow static methods
  3.     static Defaulable create( Supplier< Defaulable > supplier ) {
  4.         return supplier.get();
  5.     }
  6. }
复制代码

下面的一小段代码片段把上面的默认方法与静态方法黏合到一起。
  1. public static void main( String[] args ) {
  2.     Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
  3.     System.out.println( defaulable.notRequired() );
  4.         
  5.     defaulable = DefaulableFactory.create( OverridableImpl::new );
  6.     System.out.println( defaulable.notRequired() );
  7. }
复制代码

这个程序的控制台输出如下:
  1. Default implementation
  2. Overridden implementation
复制代码

在JVM中,默认方法的实现是非常高效的,并且通过字节码指令为方法调用提供了支持。默认方法允许继续使用现有的Java接口,而同时能够保障正常的编译过程。这方面好的例子是大量的方法被添加到java.util.Collection接口中去:stream(),parallelStream(),forEach(),removeIf(),……
尽管默认方法非常强大,但是在使用默认方法时我们需要小心注意一个地方:在声明一个默认方法前,请仔细思考是不是真的有必要使用默认方法,因为默认方法会带给程序歧义,并且在复杂的继承体系中容易产生编译错误。更多详情请参考官方文档

2.3 方法引用
方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
下面,我们以定义了4个方法的Car这个类作为例子,区分Java中支持的4种不同的方法引用。
  1. public static class Car {
  2.     public static Car create( final Supplier< Car > supplier ) {
  3.         return supplier.get();
  4.     }              
  5.         
  6.     public static void collide( final Car car ) {
  7.         System.out.println( "Collided " + car.toString() );
  8.     }
  9.         
  10.     public void follow( final Car another ) {
  11.         System.out.println( "Following the " + another.toString() );
  12.     }
  13.         
  14.     public void repair() {   
  15.         System.out.println( "Repaired " + this.toString() );
  16.     }
  17. }
复制代码

第一种方法引用是构造器引用,它的语法是Class::new,或者更一般的Class< T >::new。请注意构造器没有参数。
  1. final Car car = Car.create( Car::new );
  2. final List< Car > cars = Arrays.asList( car );
复制代码

第二种方法引用是静态方法引用,它的语法是Class::static_method。请注意这个方法接受一个Car类型的参数。
  1. cars.forEach( Car::collide );
复制代码

第三种方法引用是特定类的任意对象的方法引用,它的语法是Class::method。请注意,这个方法没有参数。
  1. cars.forEach( Car::repair );
复制代码

最后,第四种方法引用是特定对象的方法引用,它的语法是instance::method。请注意,这个方法接受一个Car类型的参数
  1. final Car police = Car.create( Car::new );
  2. cars.forEach( police::follow );
复制代码

运行上面的Java程序在控制台上会有下面的输出(Car的实例可能不一样):
  1. Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
  2. Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
  3. Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
复制代码

关于方法引用的更多详情请参考官方文档

2.4 重复注解
自从Java 5引入了注解机制,这一特性就变得非常流行并且广为使用。然而,使用注解的一个限制是相同的注解在同一位置只能声明一次,不能声明多次。Java 8打破了这条规则,引入了重复注解机制,这样相同的注解可以在同一地方声明多次。
重复注解机制本身必须用@Repeatable注解。事实上,这并不是语言层面上的改变,更多的是编译器的技巧,底层的原理保持不变。让我们看一个快速入门的例子:
  1. package com.javacodegeeks.java8.repeatable.annotations;

  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Repeatable;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;

  7. public class RepeatingAnnotations {
  8.     @Target( ElementType.TYPE )
  9.     @Retention( RetentionPolicy.RUNTIME )
  10.     public @interface Filters {
  11.         Filter[] value();
  12.     }
  13.    
  14.     @Target( ElementType.TYPE )
  15.     @Retention( RetentionPolicy.RUNTIME )
  16.     @Repeatable( Filters.class )
  17.     public @interface Filter {
  18.         String value();
  19.     };
  20.    
  21.     @Filter( "filter1" )
  22.     @Filter( "filter2" )
  23.     public interface Filterable {        
  24.     }
  25.    
  26.     public static void main(String[] args) {
  27.         for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
  28.             System.out.println( filter.value() );
  29.         }
  30.     }
  31. }
复制代码

正如我们看到的,这里有个使用@Repeatable( Filters.class )注解的注解类Filter,Filters仅仅是Filter注解的数组,但Java编译器并不想让程序员意识到Filters的存在。这样,接口Filterable就拥有了两次Filter(并没有提到Filter)注解。
同时,反射相关的API提供了新的函数getAnnotationsByType()来返回重复注解的类型(请注意Filterable.class.getAnnotation( Filters.class )经编译器处理后将会返回Filters的实例)。
程序输出结果如下:
  1. filter1
  2. filter2
复制代码

更多详情请参考官方文档

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马