函数接口
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); }