为什么要了解
在学习Lambda表达式和Optional类的使用过程中,发现多涉及到java.util.function包下边的接口。因此猜想了解函数式接口是学习Lambda表达式的基础,或者是深入学习javaf8的必经之路。本篇博客仅限抽象理解,知道其基本概念,详情请参考后续博客或其他资料详情。
简介
function包下接口称为函数式接口,又称SAM(Single Abstract Method Interface),如名、函数式接口中的
抽象方法只能有一个,但是:
- 可以包含多个来自Object类的public方法,因为所有接口或者类都默认继承Object类,包含对Object方法的实现;
- (函数式)接口允许存在default方法和静态方法,接口中引入静态方法和default方法都是java 8引入的新特性,之前不允许存在;
@FunctionalInterface//使用此注解,编译器会扫描此接口,确认只有一个抽象方法,否则会报错 public interface FunctionalTest { /** * 唯一的一个抽象方法 */ void onlyAbsMethod(); // void secAbsMethod();添加此行代码后会提示错误; /** * 继承Object的抽象方法是OK的,不会报错, * 因为所有接口和类都默认继承Object类,不违反唯一抽象规则; */ @Override boolean equals(Object obj); /**default方法 */ default void defaultMethod(){ System.out.println("method can exist in funcIntercace"); } /**静态方法 */ public static void staticMehotd(){ System.out.println("static method can exist in funcInterface"); }
用途
Lambda可以表示函数式接口的一个实现,示例:
public class BlogTest {
public static void main(String[] args) {
//Lambda表达式表示函数式接口的实现,即返回一个接口的“实例”;
//重点 TODO :Lambda表达式参数个数和类型与接口中唯一抽象方法对齐
FuncInterface funcInterface=(x,y)-> System.out.println(x+y);
//调用
funcInterface.onlyAbsMethod("du","genkui");
//java 8接口引入了default方法和静态方法,静态方法只能用接口名称调用
funcInterface.defaultMethod();
FuncInterface.staticMethod();
}
}
@FunctionalInterface
interface FuncInterface{
void onlyAbsMethod(String param1,String param2);
default void defaultMethod(){
System.out.println("default in interface,java 8");
}
static void staticMethod(){
System.out.println("static methid in interface.java 8。can be invoked by InterfaceName");
}
}
java 8中的函数式接口
在java 8中,之前很多接口都加上了@FunctionalInterface,以函数式接口的形态存在——这些接口都可以使用Lambda表达式表示其一个实例,而不用再使用内部类了;
- Runnable在java 8中变成了函数式接口,注意用Lambda表达式实现时Lambda表达式参数个数和类型与Runnable中唯一抽象方法对齐
new Thread(new Runnable() { @Override public void run() { System.out.println("implements before java 8 using anonymous inner Class"); } }).start(); /** * java 8中,Lambda表达式对Runnable接口的实现,点进去可以看见相应构造器 * TODO 重点: * 1.可以这样实现的原因是Runnable接口用@FunctionalInterface注释; * 2.TODO Lambda表达式中的参数个数了类型对应FunctionalInterface 接口唯一抽象方法的参数个数及类型 */ new Thread(()-> System.out.println("implements in java 8")).start(); //new Thread((x)-> System.out.println("implements in java 8"));报错,因为new Runnable.run()方法没有参数
疑问,如果Thread中有两个构造器参数都是@FunctionalInterface接口,用Lambda表达式实现时,会调用那个呢,情况看(java比我想象聪明一万倍啊)
两个FunctionalInterface接口的唯一抽象类参数个数相同和不同的情况下,参数类型相同和不同的情况下:
- 参数个数不同时,根据更具Lambda表达式参数个数对应到不同的函数式接口类,在对应到不同的构造器;
- 参数个数相同时,更具Lambda表达式参数类型对应到不同的...
综上,用Lambda初始化一个参数类型为函数式接口的类型实例时,Lambda表达式参数个数和类型对应到不同的函数式接口,在对应到不同的构造器;
@FunctionalInterfacepublic interface Runnable { public abstract void run();} public class FunctionalClass { private FunctionaX functionaX; private FunctionaY functionaY; private FunctionaZ functionaZ; public FunctionalClass(FunctionaX functionaX) { this.functionaX = functionaX; } public FunctionalClass(FunctionaY functionaY) { this.functionaY = functionaY; } public FunctionalClass(FunctionaZ functionaZ) { this.functionaZ = functionaZ; } public static void main(String[] args) { new FunctionalClass((String x)-> System.out.println("test")); new FunctionalClass((x,y)-> System.out.println(x+y)); new FunctionalClass((int x)-> System.out.println("test")); //TODO 会报错,因为唯一抽象方法只有一个的构造器参数有两个,编译器不知道该调用那个,必须指定类型; // TODO 但是两个参数类型一样时,指定类型也会报错 // new FunctionalClass((x)-> System.out.println("test")); }}@FunctionalInterfaceinterface FunctionaX{ void onlyAbsMethod(String mes);}@FunctionalInterfaceinterface FunctionaY { void onlyAbsMethod(String mes,String mess);}@FunctionalInterfaceinterface FunctionaZ{ void onlyAbsMethod(int mes);}
2.Callable<V>:Callable<V>和Future经常混合使用,两者都是用了泛型,后者不是函数式接口,稍后学习多线程内容是结合一块讲解
@FunctionalInterface public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception;}
3.java.util.function包下的函数式接口,常用的有Consumer、Predicate个 还有Function;Optional类的实现对着三个多有涉及;
Consumer,如命为“消费者”,被Optional的ifPresent(XX)调用。此函数的作用是如果Optional中有元素,则调用Consumer
消费者类处理此元素。由于Consumer消费者是一个函数式接口,因此可以用Lambda表达式返回其一个实例,注意表达式与消费者接口唯一抽象方法参数个数和类型对齐。源码如下
//value为Optional保存的元素,也是消费者接口唯一抽象方法的入参,也是Lambda式子的参数
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
ifPresent()函数中的value值是Optional保存的元素,也是消费者接口唯一抽象方法的入参(由此可知用到了泛型),也是声明消费者Consumer接口“实例”时Lambda表达式的参数
4.Consumer/消费者接口有两个方法:
void
accept(
T
t) 和
default
Consumer<
T
> andThen(Consumer<?
super
T
> after){},前者是唯一抽象方法,Lambda表达式参数应与其对齐;前者是处理参数,后者指作为参数可以再Consumer的accept方法处理完入参后再处理一次。其源码和相关注释讲解如下
/**
* Lambda表达式表示处理Optional元素的逻辑
*/
void accept(T t);
/**
* @param after 在accpet(T t)处理完参数后的第二个逻辑,继续对参数进行处理
*/
default java.util.function.Consumer<T> andThen(java.util.function.Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> {
accept(t); //首先进行此Consumer类的处理逻辑;
after.accept(t);//然后anThen(Consumer X)中参数逻辑在对参数进行处理
};
}
如下示例为使用两个方法,注意Iterator的
forEachRemaining()方法1.使用函数式接口处理其中元素,而且Iterator遍历完之后不能重置;
//将一个字符链表装如Optional中 Optional<Iterator<String>> optiExist= Optional.of((Arrays.asList("du","gen","kui").iterator())); //没元素 Optional optEmp= Optional.empty(); //测试Consummer的两个方法 optiExist.ifPresent(x->{ System.out.println(x.getClass()); System.out.println("测试{}符号");} ); optEmp.ifPresent(x-> { System.out.println(x.getClass()); System.out.println("测试{}符号"); }); //测试AndThen方法:TODO 重点:在Consummer<>尖括号中指定Lambda表达式、也是抽象方法参数的类型 Consumer<Iterator<String>> consumer=x->x.forEachRemaining( y->System.out.println(y+"X")); optiExist.ifPresent(consumer.andThen( x-> System.out.println("在遍历元素之后调用")) );
//补充 forEachRemaining源码 default void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); while (hasNext()) action.accept(next()); }
5.Predicte,如名“断言”,唯一抽象方法test(T t);返回值是Boolean类型,主要用于检查元素是否符合某些条件。还可以用or、and连接条件,也可以用Predicate<T> negate() 方法取其相反断言。代码示例如下;//测试使用链表 List<Integer> arrayList=new ArrayList<>(); arrayList.add(-1); arrayList.add(0); arrayList.add(1); //检测Integrate类型元素与0的关系; Predicate<Integer> bigThanZero=x->x>0; Predicate<Integer> equalZero=x->x==0; //过滤掉链表中大于0的数打印,然后测试过滤掉不大于0的数(注意不是小于),然后打印 arrayList.stream().filter(bigThanZero).forEach(a-> System.out.print(a)); System.out.println(); arrayList.stream().filter(bigThanZero.negate()).forEach(x-> System.out.print(x+",")); System.out.println(); //or和and arrayList.stream().filter(bigThanZero.or(equalZero)).forEach(a-> System.out.print(a+","));//大于或者等于0 System.out.println(); arrayList.stream().filter(bigThanZero.negate().and(equalZero)).forEach(a->System.out.println(a+","));//不大于且等于0,只有0
/** isEqual(Object)源码分析,使用到了泛型,注意其返回值类型 * @param targetRef 对比的对象 * @return 返回一个Lambda表达式,表示对Predicate的初始化 */ static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull//等价于 obj->Object.isNull(obj); : object -> targetRef.equals(object); }
//测试代码:内层的(Predicate.isEqual(1) 返回Predicate实例: x->x.equals(1); arrayList.stream().filter(Predicate.isEqual(1)).forEach(x-> System.out.println(x+" equal"));
Function唯一抽象方法是
R
apply(
T
t);
还有
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); }
和
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); }来指定了顺序执行apply类型逻辑。
还有一个不知道为什么存在的方法(原样返回,用于复制?!)
static <T> Function<T, T> identity() { return t -> t; }
Lambda表达式可以是多句,用中括号括起来,如
x->{expressionX;expressionB;}