函数式接口
一、函数式接口的定义
函数式接口:函数式接口中有且仅有一个抽象方法,这个抽象方法的意义在于表达某种行为。
以下情况是满足函数式接口定义的:
1. 接口中可以有多个默认实现,但是默认实现需要用default关键字显示标注。
@FunctionalInterface
//此注解用于显式表示一个接口是否满足函数式接口的定义,如果不满足定义会标红
public interface MyInterface {
String getName();
//这是默认实现的方法,并不违反函数接口的规则。
default int getAge(){
return 1;
}
}
2. 接口可以包含过个静态方法。
@FunctionalInterface
public interface MyInterface {
String getName();
//这是静态的方法,并不违反函数接口的规则。
static int getAge() {
return 1;
}
}
3. 接口可以包含和Object的public一样方法签名的方法,但是此方法需要是抽象的。
@FunctionalInterface
public interface MyInterface {
String getName();
//该方法的方法签名与Object的相同
//并且是抽象的,因为实现此接口的实现类,必然会提供方法的实现。
int hashCode();
}
二、函数式接口的表示
函数式接口的实例有三种方式创建:Lambda表达式、方法引用和构造方法引用。因为函数式接口只有一个抽象方法,所以Lambda表达式实际代表的行为就是接口中的抽象方法,也从侧面说明了Lambda表达式不能脱离函数式接口的语境。
函数式接口与Lambda表达式有着天然的联系,Lambda表达式的基本结构是
(Type1 arg1,Type2,arg2,...) -> {body}
1. 如果参数可以通过类型推导出来,那么参数类型可以不写。(可以先不写试试)
2. 如果参数只有一个,可以将括号省略。
3. body如果只有一行代码,可以省略花括号。注意的是一行代码的表示有表达式和语句之分,需要配合return、分号、花括号的情况。
4. 为了便于理解和说明,这里将Lambda表达式的按着其参数和返回值类型进行描述。
(int a,int b)->a+b 的类型为(int,int)->int
(int a,int b)->println(a+b) 的类型为(int,int)->Unit(不返回值)
三、JDK8中的常见的函数式接口
Consumer:接受一个参数,不返回结果
/**
* 这个函数式接口代表一种操作行为:无返回结果的单一参数操作
* 接口中的抽象方法,就是操作
* 仍包含一个默认方法
*/
@FunctionalInterface
public interface Consumer<T> {
/**
* 对给定的参数进行操作
*/
void accept(T t);
/**
* 返回一个组合的Consumer。对t参数执行完给定的行为后,再去执行after行为。
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
以集合遍历为例
public class Main {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
//forEach函数参数类型是Consumer,对集合的每个元素执行accept行为(第一处)
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
//此处的行为就是对元素进行打印,
System.out.println(integer);
}
});
//Lambda表达式 就是描述的接口的抽象方法
//本例中,接口的抽象方法 (接受一个参数,执行一个无返回结果的操作)
//也就是(T t)->Unit,式子中的item可以命名为任意名字
list.forEach(item -> System.out.println());
}
}
//第一处的源码如下
//调用的是Iterable接口的默认方法forEach
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
//遍历集合的每一个元素
for (T t : this) {
//调用接口的accept行为方法,参数是集合的元素
action.accept(t);
}
}
//以上的源码就说明了 Consumer代表一种抽象的行为,集合的元素去参与这个行为,开发者需定义出具体行为。
Function接口:接受一个参数,返回一个结果
/**
* 接受一个t参数返回一个R类型的结果,T与R可以相同也可以不同
*/
@FunctionalInterface
public interface Function<T, R> {
/**
* t参数去执行给定的行为
*/
R apply(T t);
/**
* 返回一个符合函数,在本实例行为执行前,先去执行before的行为,将before的结果作为本实例行为的输入
* 因此,before的结果类型一定要是T类型的子类或本身。
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
//先执行before,再执行本实例的行为
return (V v) -> apply(before.apply(v));
}
/**
* 返回一个符合函数,在本实例行为执行后,紧接着去执行after的行为,将本实例的结果作为after行为的输入
* 因此,after的输入一定是本实例结果的父类型或本身
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
//先执行本实例,再执行after
return (T t) -> after.apply(apply(t));
}
/**
* 返回一个参数无操作的行为
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
以字符串取小写为例
public class FunctionTest {
public static void main(String[] args) {
FunctionTest functionTest = new FunctionTest();
//匿名函数 (第一处)
functionTest.operation("Hello world", new Function<String, String>() {
@Override
public String apply(String str) {
return str.toLowerCase();
}
});
//表达式
functionTest.operation("Hello world", item -> item.toLowerCase());
//Lambda表达式声明函数式接口的实例
Function<String, String> function = item -> item.toLowerCase();
functionTest.operation("Hello world",function);
//语句
functionTest.operation("Hello world", item -> {
return item.toLowerCase();
});
//以上几种方式 都是对Function的实践。定义一种行为:对字符串进行小写并返回
//输入类型和输出类型都是字符串。
//开发者自己定义操作的行为,调用的方法都是operation。
}
public String operation(String str, Function<String, String> function) {
return function.apply(str);
}
}
以第一处为例分析其执行流程
①调用operation方法,参数是"Hello World"、匿名类。
②调用参数function的apply方法,实际调用的是传入的匿名类的apply方法。
③apply方法的参数是传入的"Hello World",执行取小写操作。
综上:开发者需要的是在方法的调用处,定义出需执行的行为,传递的是行为。定义行为的方式有匿名类,Lambda表达式,方法引用等,Lambda表达式更加简洁。
BiFunction:接受两个参数,返回一个结果
/**
* 是Function思想的特殊化,接受两个参数,返回一个R类型结果
*/
@FunctionalInterface
public interface BiFunction<T, U, R> {
/**
* 对t和u参数执行给定的行为
*/
R apply(T t, U u);
/**
* 返回一个复合的函数,在执行完本实例行为后,将本实例的结果作为after行为的输入,执行after行为。
*/
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
//先执行本实例行为,在执行after行为
return (T t, U u) -> after.apply(apply(t, u));
}
}
以数字的四则运算操作为例
public class FunctionTest2 {
public static void main(String[] args) {
FunctionTest2 test = new FunctionTest2();
//Lambda表达式创建出BiFunction实例
//开发者需自己定义出 执行的行为,并传递给compute函数
//compute函数执行具体的行为
test.compute(2, 3, (a, b) -> a + b);
test.compute(2, 3, (a, b) -> a - b);
test.compute(2, 3, (a, b) -> a * b);
test.compute(2, 3, (a, b) -> a / b);
//先执行两个数字相乘,将结果执行平方(第一处)
test.compute1(2, 3, (a, b) -> a * b, a -> a * a);
}
public int compute(int a, int b, BiFunction<Integer, Integer, Integer> function) {
return function.apply(a, b);
}
public int compute1(int a, int b, BiFunction<Integer, Integer, Integer> biFunction, Function<Integer, Integer> function) {
return biFunction.andThen(function).apply(a, b);
}
}
第一处的执行流程
①将Lambda表达式构建的接口实例传入compute1方法。
②调用BiFunction(数字的乘积)的andTen方法,参数是 function(数字的平方)
③调用BiFunction(数字的乘积)的apply方法,计算出实际的结果
④调用function(数字的平方)的apply方法,参数是上一步的结果
⑤返回结果
Predicate:判断参数是否满足 断言
/**
* 判断参数是否满足 断言(也就是布尔值)
*/
@FunctionalInterface
public interface Predicate<T> {
/**
* 对给定的参数t执行测试行为
*/
boolean test(T t);
/**
* 与操作
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/**
*非操作
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}
/**
* 或操作
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
*测试两个实例是否相同(equals相等)
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
以字符串判断equals为例
public class PredicateTest {
public static void main(String[] args) {
PredicateTest predicateTest = new PredicateTest();
//第一处
predicateTest.predicateString("Hello", item -> item.equals("test"));
//第二处
Predicate<String> predicate = Predicate.isEqual("test");
predicate.test("Hello");
//以上两个例子并无不同,Predicate的静态isEqual方法只是帮我们写了断言条件
//第一处的流程分析
//①调用predicateString方法,参数是“Hello”和Lambda表达式生成的接口实例
//②调用接口实例的test方法,就是item -> item.equals("test")
// item就是“Hello”
//③返回断言结果false
//第二处的流程分析
//①调用静态方法isEqual("test"),生成接口实例
//②调用test方法,参数是“Hello”
//③test的实现是isEqual,返回结果false
}
public boolean predicateString(String str, Predicate<String> predicate) {
return predicate.test(str);
}
}
Supplier:返回一个结果即可,无参数
/**
*返回一个结果
*/
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*/
T get();
}
四、总结以上接口
Consumer<T>
void accept(T t);
接受一个参数,不返回结果
Function<T,R>
R apply(T t);
接受一个类型的参数,返回一个结果
可以通过默认的compose和andThen方法复合函数
BiFunction<T, U, R>
R apply(T t, U u);
接受两个类型的参数,返回一个结果
Predicate<T>
boolean test(T t);
接受一个参数,返回一个Boolean值
Supplier<T>
T get()
不接受参数返回一个结果
五、小结
函数式的参数代表的是一种行为,开发者传递的是某种操作
网友评论