Java8---2.函数接口的默认方法和静态方法

1.注解FunctionInterface

因为函数接口是只有一个方法的接口,指定编译器检查,该接口只有一个抽象方法,是函数接口(只允许一个抽象方法,但是可以有默认方法和静态方法

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

不过有一点需要注意,默认方法和静态方法不会破坏函数式接口的定义,因此如下的代码是合法的。

@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();

    default void defaultMethod() {            
    }        
}

2.接口的默认方法

Java 8使用两个新概念扩展了接口的含义:默认方法和静态方法默认方法使得接口有点类似traits,不过要实现的目标不一样。默认方法使得开发者可以在 不破坏二进制兼容性的前提下,往现存接口中添加新的方法,即不强制那些实现了该接口的类也同时实现这个新加的方法。

默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要。接口提供的默认方法会被接口的实现类继承或者覆写,例子代码如下:

private interface Defaulable {
    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()。DefaultableImpl类实现了这个接口,同时默认继承了这个接口中的默认方法;OverridableImpl类也实现了这个接口,但覆写了该接口的默认方法,并提供了一个不同的实现。

3.接口的静态方法

public interface Defualable{
		default String notRequired(){
			return "默认接口";
		}
	}
	public static class DefaultableImpl implements Defualable{//使用默认方法的类
		
	}
	public static class OverridableImpl implements Defualable{//覆盖接口方法的实现类
		public String notRequired(){
			return "重写方法";
		}
	}
	public interface DefaultFactory{
		static Defualable create(Supplier<Defualable> supplier ){//接口的静态方法 
			return supplier.get();//supplier接口是一个工厂接口 只有一个get方法
		}
	}
	public static void main(String[] args){
		Defualable d=DefaultFactory.create(DefaultableImpl::new);
		System.out.println(d.notRequired());
		Defualable d2=DefaultFactory.create(OverridableImpl::new);
		System.out.println(d2.notRequired());


这段代码的输出结果如下:默认接口 重写方法

由于JVM上的默认方法的实现在字节码层面提供了支持,因此效率非常高。默认方法允许在不打破现有继承体系的基础上改进接口。该特性在官方库中的应用是:给java.util.Collection接口添加新方法,如stream()parallelStream()forEach()removeIf()等等。

尽管默认方法有这么多好处,但在实际开发中应该谨慎使用:在复杂的继承体系中,默认方法可能引起歧义和编译错误。

4、方法引用

可以直接引用现存的方法、Java类的构造方法或者实例对象。方法引用和Lambda表达式配合使用,使得java类的构造方法看起来紧凑而简洁,没有很多复杂的模板代码。

西门的例子中,Car类是不同方法引用的例子,可以帮助读者区分四种类型的方法引用。

public static class Car {
    public static Car create( final Supplier< Car > supplier ) {//引用类型是类的构造器  Car::new
        return supplier.get();
    }              

第一种方法引用的类型是构造器引用,语法是Class::new,或Class<T>::new。注意:这个构造器没有参数。

		final Car car = Car.create( Car::new );
		final List< Car > cars = Arrays.asList( car );
publicstaticvoid collide(final Car car){//静态方法应用 Car::collide System.out.println("Collided " + car.toString() ); }

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

		cars.forEach( Car::collide );
publicvoidfollow(final Car another){//某个类的成员方法的引用 System.out.println("Following the " + another.toString() ); }

第三种方法引用的类型是某个类的成员方法的引用,语法是Class::method,注意,这个方法没有定义入参:

		cars.forEach( Car::repair );
publicvoidrepair(){ //某个类实例的应用 System.out.println("Repaired " +this.toString() ); }}

第四种方法引用的类型是某个实例对象的成员方法的引用,语法是instance::method。注意:这个方法接受一个Car类型的参数:

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

运行上述例子,可以在控制台看到如下输出(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
方法引用 等价的lambda表达式
String::valueOf
x -> String.valueOf(x)
Object::toString
x -> x.toString()
x::toString
() -> x.toString()
ArrayList::new
() -> new ArrayList<>()

5、重复注解(了解)

自从Java 5中引入注解以来,这个特性开始变得非常流行,并在各个框架和项目中被广泛使用。不过,注解有一个很大的限制是:在同一个地方不能多次使用同一个注解。Java 8打破了这个限制,引入了重复注解的概念,允许在同一个地方多次使用同一个注解。

在Java 8中使用@Repeatable注解定义重复注解,实际上,这并不是语言层面的改进,而是编译器做的一个trick,底层的技术仍然相同。可以利用下面的代码说明:

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() );
        }
    }
}

正如我们所见,这里的Filter类使用@Repeatable(Filters.class)注解修饰,而Filters是存放Filter注解的容器,编译器尽量对开发者屏蔽这些细节。这样,Filterable接口可以用两个Filter注解注释(这里并没有提到任何关于Filters的信息)。

另外,反射API提供了一个新的方法:getAnnotationsByType(),可以返回某个类型的重复注解,例如Filterable.class.getAnnoation(Filters.class)将返回两个Filter实例,输出到控制台的内容如下所示:

filter1
filter2










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值