Java8之函数接口

函数接口

Java8新特性提供了函数式接口,用于更好的支持函数式编程。

定义

所谓函数式接口就是只有一个抽象方法的接口。Java8中可以通过注解(@FunctionalInterface)来限定它(即便不加注解,只有一个抽象方法默认也是函数式接口)。

比如:

public interface MyInterface {
    void myFunction();
}

加上注解@FunctionalInterface后,当超出一个抽象方法时,直接编译报错。

@FunctionalInterface
public interface MyInterface {
    void myFunction();
}

默认方法

Java8运行接口有默认方法,用default 关键字修饰即可,当然可以有多个默认方法。注意,默认方法只能是public,且默认是public。

@FunctionalInterface
public interface MyInterface {
    void myFunction();
    
    default void defaultFunction1() {
        System.out.println("Interface : default function1");
    }

    default void defaultFunction2() {
        System.out.println("Interface : default function2");
    }
}


静态方法

Java8支持接口有静态方法,另外调用时,使用接口名调用静态方法。注意,默认方法只能是public,且默认是public。
@FunctionalInterface
public interface MyInterface {
    void myFunction();
    static void staticFunction() {
        System.out.println("Interface : staticFunction");
    }
}
与类的静态方法调用方式一样:
MyInterface.staticFunction();

使用

虽然函数式接口提供了这些功能,不过使用时需要更加小心。比如多个接口的继承和实现问题。下面用一个例子说明下,先看类图:


这里有三个接口,SubInterface extends 另外两个接口。下边是各个类的代码:

InterfaceA 拥有两个默认方法,一个静态方法和一个抽象方法。

@FunctionalInterface
public interface InterfaceA {
    default void defaultFunction() {
        System.out.println("InterfaceA : defaultFunction");
    }

    default void defaultFunction2() {
        System.out.println("InterfaceA : defaultFunction 2");
    }

    static void staticFunction() {
        System.out.println("InterfaceA : staticFunction");
    }

    void functionNormal();
}

函数接口InterfaceB类似,只有一个默认方法,不过该默认方法与InterfaceA的一个方法签名一样。

@FunctionalInterface
public interface InterfaceB {
    default void defaultFunction() {
        System.out.println("InterfaceB : defaultFunction");
    }

    static void staticFunction() {
        System.out.println("InterfaceB : staticFunction");
    }

    void functionNormal();
}

而函数接口SubInterface 同时继承了上边两个接口。

@FunctionalInterface
public interface SubInterface  extends InterfaceA, InterfaceB{
    default void defaultFunction() {// 两个默认方法一样,必须重写。
        this.defaultFunction2();// 默认方法 可以被继承
        System.out.println("SubInterface : defaultFunction");
    }

    static void staticFunction() {
        InterfaceA.staticFunction();
        InterfaceB.staticFunction();
        System.out.println("SubInterface : staticFunction");
    }

    void functionNormal();
}

注意:
1.默认方法可以被继承,因此SubInterface 拥有 InterfaceA 的默认方法 defaultFunction2()。
2.因为多继承问题,两个默认方法重名,此时子接口必须重写该冲突默认方法,否则编译报错。

现在类MyImplClass 实现了上边的子接口:

public class MyImplClass implements SubInterface {
    @Override
    public void defaultFunction() {
        System.out.println("default Function can be implemented by class.");
    }

    @Override
    public void functionNormal() {
        System.out.println("MyImplClass : functionNormal");
    }
}

发现:默认方法可以被类重写。

测试下我们的类:

    public static void main(String[] args) {
        SubInterface m = new MyImplClass();
        SubInterface.staticFunction();
        m.functionNormal();
        m.defaultFunction();

        System.out.println("----------------------");

        SubInterface m2 = new SubInterface(){
            @Override
            public void functionNormal() {
                System.out.println("InterfaceB : functionNormal self implement.");
            }
        };
        m2.functionNormal();
        m2.defaultFunction();
    }

输出如下:
InterfaceA : staticFunction
InterfaceB : staticFunction
SubInterface : staticFunction
MyImplClass : functionNormal
default Function can be implemented by class.
----------------------
InterfaceB : functionNormal self implement.
InterfaceA : defaultFunction 2
SubInterface : defaultFunction


四大函数接口

Java8页内置了四个比较重要的函数式接口供我们使用:Predicate。简单举个例子:

Predicate

Predicate接口用于断言,也叫谓语接口,简单的判断“是”与“不是”:收一个参数,返回一个boolean值。因此多用于判断和过滤。

    // predicate<T> 断言型接口:
    public void process() {
        List<Integer> list = Arrays.asList(21, 34, 44, 55, 66);
        list = filterOutElement(list, v -> v > 40);
        list.forEach(System.out::println);
    }

    // 过滤出满足条件的元素
    public List<Integer> filterOutElement(List<Integer> list, Predicate<Integer> predicate) {
        List<Integer> resultList = new ArrayList<>();
        for (Integer temp : list) {
            if (predicate.test(temp))
                resultList.add(temp);
        }
        return resultList;
    }

Function

即函数型接口。函数式编程中函数是可以作为参数的。该接口的作用即是如此。这样函数就可以被传递与复用。说白了就是数学中的 复合函数:f(g(x))。 g(x) 作为f(y)的参数。
    //Function<T,R>函数型接口
    public void formatUpper() {
        String str = formatHandler("My text input", s -> s.toUpperCase());
        System.out.println(str);
    }

    // 根据传入的函数表达式进行处理
    public String formatHandler(String str, Function<String, String> fun) {
        return fun.apply(str);
    }
这里 formatHandler 就是 f(y),Lambda 表达式 s -> s.toUpperCase() 就是 g(x) .


Supplier

从名字就可以知道,是一个提供者。拥有 get() 方法,用它可以产生或者获取我们要求的数据。

    // Supplier<T>供给型接口:
    public void getRandom() {
        List<Integer> list = getIntegerList(10, () -> (int) (Math.random() * 100));
        list.forEach(System.out::println);
    }

    // 提供产生指定个数的Integer 生成器
    public List<Integer> getIntegerList(int size, Supplier<Integer> sup) {
        List<Integer> resultList = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            resultList.add(sup.get());
        }
        return resultList;
    }

Consumer

跟上边相反,该函数式接口用于消费数据。比如对于获取的数据,做下一步的处理。

    //Consumer<T> 消费型接口
    public void consumerGivenString() {
        List<String> result = Arrays.asList("Hust", "ZW", "Kaka");
        outputHandler(result, strs -> strs.forEach(str -> System.out.println(str)));
    }

    public void outputHandler(List<String> strs, Consumer<List<String>> con) {
        con.accept(strs);
    }





猜你喜欢

转载自blog.csdn.net/hustzw07/article/details/80792708