目录

  • 1、 函数式接口
  • 1.1 概念
  • 1.2 例子
  • 1.3 四种函数式接口
  • 消费型接口
  • 供给型接口
  • 函数型接口
  • 断言型接口
  • 3.4 默认方法

1、 函数式接口

1.1 概念

函数式接口是只有一个方法的接口,用作lambda表达式的类型。

具体就是说,注解在Inteface上,且interface里只能有一个抽象方法,可以有default方法。

因为从语义上来讲,一个函数式接口需要通过一个逻辑上的方法表达一个单一函数。那理解这个单一就很重要了,单一不是说限制你一个interface里只有一个抽象方法,单一是多个方法的其他方法需要是继承自Objectpublic方法,或者你要想绕过,就自己实现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时, 实现类按照约定也要修改实现,而Java8API对现有接口也添加了很多方法,比如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表达式作为循环体。

一个小小的程序员