1、 函数式接口
1.1 概念
函数式接口是只有一个方法的接口,用作lambda
表达式的类型。
具体就是说,注解在Inteface
上,且interface
里只能有一个抽象方法,可以有default
方法。
因为从语义上来讲,一个函数式接口需要通过一个逻辑上的方法表达一个单一函数。那理解这个单一就很重要了,单一不是说限制你一个interface
里只有一个抽象方法,单一是多个方法的其他方法需要是继承自Object
的public
方法,或者你要想绕过,就自己实现default
。
函数式接口自己本身一定是只有一个抽象方法。同时,如果是Object
类的public
方法,也是不允许的
前面写的例子就是一个函数式接口,来看看jdk中的Runnable
源码
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
这里只有一个抽象方法run,实际上你不写public abstract
也是可以的,在接口中定义的方法都是public abstract
的。 同时也使用注解@FunctionalInterface
告诉编译器这是一个函数式接口,当然你不这么写也可以,标识后明确了这个函数中 只有一个抽象方法,当你尝试在接口中编写多个方法的时候编译器将不允许这么干。
1.2 例子
@FunctionalInterface
public interface Demo5 {
}
这样编译器肯定是报错的,会显示没有target method
,因为函数式接口必须要又一个抽象方法;
@FunctionalInterface
public interface Demo5 {
void run();
}
这个时候,一个函数式接口就Ok了;
@FunctionalInterface
public interface Demo5 {
void run();
void go();
}
编译器报错Multiple non-overriding abstract methods found in interface
,因为函数式接口只可以有一个抽象方法;
@FunctionalInterface
public interface Demo5 {
boolean equals(Object obj);
}
编译器报错no target method
,因为equals
方法是Object
类的public
的
@FunctionalInterface
public interface Demo5 {
@Override
boolean equals(Object obj);
void run();
}
一个抽象方法,一个Object的public方法,相安无事
@FunctionalInterface
public interface Demo5 {
Object clone();
void run();
}
编译器报错,因为clone()
是Object
类里面protected
方法
@FunctionalInterface
public interface Demo5 {
void run();
/**
* 非静态方法
*/
default void test() {
System.out.println("我是 default方法");
}
/**
* 静态方法
*/
public static void test2() {
System.out.println("333");
}
}
这也是一个合格的函数式接口,因为从java8开始,接口中可以存在静态方法(只可以是public
修饰)和非静态方法(只可以使default
修饰)
1.3 四种函数式接口
接口 | 参数 | 返回值 | 示例 | 类别
---|---|---|---|---|---|---
Consumer | T | void | 输出一个值|消费型接口
Supplier | None | T | 工厂方法 | 工厂方法
Function | T | R | 获得Artist对象的名字|函数型接口
Predicate | T | boolean | 这张唱片已经发行了吗|断言型接口
消费型接口
public static void donation(Integer money, Consumer<Integer> consumer) {
consumer.accept(money);
}
donation(100, x -> System.out.println("捐赠了:" + x));
供给型接口
// 产生一些整数放到集合中
public static List<Integer> supply(Integer num, Supplier<Integer> supplier) {
List<Integer> list = new ArrayList<>();
for (int x = 0; x <= num; x++) {
list.add(supplier.get());
}
return list;
}
List<Integer> list = supply(10, () -> (int) (Math.random() * 1000));
函数型接口
public static Integer convert(String str, Function<String, Integer> function) {
return function.apply(str);
}
Integer a = convert("12", x -> Integer.parseInt(x));
断言型接口
// 将满足条件的字符串放到集合中
public static List<String> filter(List<String> fruits, Predicate<String> predicate) {
List<String> resultList = new ArrayList<>();
for (String item : fruits) {
if (predicate.test(item)) {
resultList.add(item);
}
}
return resultList;
}
List<String> list1 = new ArrayList<>();
list1.add("苹果");
list1.add("香蕉");
list1.add("西瓜");
list1.add("西红柿");
List<String> f = filter(list1, x -> x.length() == 2);
3.4 默认方法
在Java
语言中,一个接口中定义的方法必须由实现类提供实现。但是当接口中加入新的API
时, 实现类按照约定也要修改实现,而Java8
的API
对现有接口也添加了很多方法,比如List接口中添加了sort
方法。 如果按照之前的做法,那么所有的实现类都要实现sort
方法,JDK
的编写者们一定非常抓狂
幸运的是我们使用了Java8
,这一问题将得到很好的解决,在Java8
种引入新的机制,支持在接口中声明方法同时提供实现。 这令人激动不已,你有两种方式完成:
1.在接口内声明静态方法
2.指定一个默认方法
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
翻阅List
接口的源码,其中加入一个默认方法default void sort(Comparator<? super E> c)
。
在返回值之前加入default关键字,有了这个方法我们可以直接调用sort方法进行排序
List<Integer> list = Arrays.asList(2, 7, 3, 1, 8, 6, 4);
list.sort(Comparator.naturalOrder());
System.out.println(list);
Comparator.naturalOrder()
是一个自然排序的实现,这里可以自定义排序方案。你经常看到使用Java8操作集合的时候可以直接foreach的原因也是在Iterable
接口中也新增了一个默认方法:forEach
,该方法功能和 for
循环类似,但是允许 用户使用一个Lambda
表达式作为循环体。