函数式接口
概述
函数式接口在Java中是指:有且仅有一个抽象方法的接口。
Lambda表达式是用来实现SAM接口的,所谓SAM就是Single Abstract Method,即该接口中只有一个抽象方法需要实现,当然该接口可以包含其他非抽象方法。
从应用层面来讲,Java中的Lambda表达式可以看做是接口对应的匿名内部类的简化格式,但是二者在原理上不同。
@FunctionalInterface注解
与 @Override
注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface
,该注解可用于一个接口的定义上:
@FunctionalInterface
public interface MyFunctionalInterface {
void myMethod();
}
一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。不过,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。
JDK1.8之前,核心类库中就已经存在很多SAM接口了,例如:
(1)java.lang.Runnable
(2)java.util.concurrent.Callable
(3)java.util.Comparator
(4)java.lang.Comparable
(5)java.lang.Iterable
(6)java.io.FileFilter等
但是在JDK1.8,只有部分方法加了 @FunctionalInterface
,那些没有加 @FunctionalInterface
的SAM接口,存在将来增加抽象方法变成非SAM接口的风险,因此建议只对加了 @FunctionalInterface
的接口使用Lambda表达式实现。
JDK1.8在 java.util.function
包增加了很多函数式接口,不过他们可以归纳为四类:供给型接口、消费型接口、功能型接口、判断型接口,一共43个
供给型接口
这类接口名以单词Supplier结尾,Supplier:供应者。对应的Lambda表达式需要“对外提供”数据。
这类接口的抽象方法特点:无参,有返回值;
典型代表—Supplier接口
java.util.function.Supplier<T>
接口,对应的Lambda表达式需要对外提供一个符合泛型类型的对象数据。接口定义:
@FunctionalInterface
public interface Supplier<T> {
T get();
}
抽象方法 : T get()
用来获取一个泛型参数指定类型的对象数据。
代码示例:
public class SupplierDemo {
public static void main(String[] args) {
String strA = "Hello ";
String strB = "World";
//传参:Supplier<String> supplier = () -> strA + strB;
//调用重写的get方法:get() {return strA + strB;}
String result = getString(() -> strA + strB);
System.out.println(result);
}
static String getString(Supplier<String> supplier) {
return supplier.get();
}
}
求数组元素最大值
使用Supplier
接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值,接口的泛型使用java.lang.Integer
类。
代码示例:
public class IntArrayTest {
public static void main(String[] args) {
int[] array = {10, 5, 12, 3, 7, 15};
pringMax(() -> {
int max = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
return max;
});
}
static void pringMax(Supplier<Integer> supplier) {
int max = supplier.get();
System.out.println(max);
}
}
消费型接口
这类接口名称以单词Consumer结尾,Consumer:消费者。对应的Lambda表达式需要接收参数来完成功能。
这类接口的抽象方法特点:有参、无返回值
典型代表—Consumer接口
java.util.function.Consumer<T>
:接收一个对象,来执行相关操作,其数据类型由泛型参数决定。
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
抽象方法:void accept(T t)
接收一个对象,来执行相关操作
public class ConsumerDemo {
public static void main(String[] args) {
String str = "Hello world";
//字符串转为小写
convert(s -> System.out.println(s.toLowerCase()), str);
}
static void convert(Consumer<String> consumer, String str) {
consumer.accept(str);
}
}
默认方法:andThen,如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以:接收一个数据的时候,首先做一个操作,然后再做一个操作,实现组合。源码:
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
代码示例:大小写转换
public class ConsumerDemo {
public static void main(String[] args) {
String str = "Hello world";
//Consumer<String> c1 = s1 -> System.out.println(s1.toLowerCase());
//Consumer<String> c2 = s2 -> System.out.println(s2.toUpperCase());
convert(s1 -> System.out.println(s1.toLowerCase()),
s2 -> System.out.println(s2.toUpperCase()), str);
}
static void convert(Consumer<String> c1, Consumer<String> c2, String str) {
c1.accept(str);
c2.accept(str);
//andThen方法返回值为Consumer<T>接口的实现类对象:(T t) -> { accept(t); after.accept(t); }
//先小写后大写:c1.accept(str); c2.accept(str);
c1.andThen(c2).accept(str);
//先大写后小写:c2.accept(str); c1.accept(str)
c2.andThen(c1).accept(str);
}
}
上面代码中,c1.andThen(c2)
返回Consumer接口的实现类对象,该对象调用accept(str),此时重写的accept(str)方法的方法体为{c1.accept(str); c2.accept(str)}
判断型接口
这类接口名称以单词Predicate结尾,Predicate:断言、判断。对某种类型的数据进行判断,从而得到一个boolean值结果。
这类接口的抽象方法特点:有参、返回值类型是boolean
典型代表—Predicate接口
接口定义:
@FunctionalInterface
public interface Predicate<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);
}
}
抽象方法:boolean test(T t),用于条件判断的场景,条件判断的标准是传入的Lambda表达式逻辑
代码示例:
输出正数数组中的所有偶数:
public class PredicateTest {
public static void main(String[] args) {
int[] array = {0, 1, 2, 3, 4, 6, 7, 10, 15, 20};
System.out.println("所有偶数:");
eval(array, n -> n%2 == 0);
}
static void eval(int[] array, Predicate<Integer> p) {
for (int i : array) {
if (p.test(i)) {
System.out.println(i);
}
}
}
}
默认方法:and,既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实现“并且”的效果时,可以使用default方法 and ,源码:
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
输出大于等于6所有偶数:
public class PredicateTest {
public static void main(String[] args) {
int[] array = {0, 1, 2, 3, 4, 6, 7, 10, 15, 20};
System.out.println("大于等于6所有偶数:");
eval(array, m -> m >= 6, n -> n%2 == 0);
}
static void eval(int[] array, Predicate<Integer> p1, Predicate<Integer> p2) {
for (int i : array) {
if (p1.and(p2).test(i)) {
System.out.println(i);
}
}
}
}
默认方法:or,与 and 的“与”类似,默认方法 or 实现逻辑关系中的或,源码:
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
输出大于6的整数或偶数:
public class PredicateTest {
public static void main(String[] args) {
int[] array = {0, 1, 2, 3, 4, 6, 7, 10, 15, 20};
System.out.println("大于6的整数或偶数:");
eval(array, m -> m >= 6, n -> n%2 == 0);
}
static void eval(int[] array, Predicate<Integer> p1, Predicate<Integer> p2) {
for (int i : array) {
if (p1.or(p2).test(i)) {
System.out.println(i);
}
}
}
}
默认方法:negate,实现逻辑关系中的非,源码:
default Predicate<T> negate() {
return (t) -> !test(t);
}
输出不大于7的整数:
public class PredicateTest {
public static void main(String[] args) {
int[] array = {0, 1, 2, 3, 4, 6, 7, 10, 15, 20};
System.out.println("不大于7的整数:");
eval(array, n -> n > 7);
}
static void eval(int[] array, Predicate<Integer> p) {
for (int i : array) {
if (p.negate().test(i)) {
System.out.println(i);
}
}
}
}
功能型接口
这类接口名称以单词Function或Operator结尾,用于完成某项功能并返回数据
这类接口的抽象方法特点:有参数、有返回值
典型代表—Function接口
java.util.function.Function<T,R>
接口用来根据一个类型的数据得到另一个类型的数据
接口定义:
@FunctionalInterface
public interface Function<T, R> {
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));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
抽象方法:R apply(T t)
Function
接口中最主要的抽象方法为:R apply(T t)
,根据类型T的参数获取类型R的结果。
类似于数学中的函数: y = f ( x ) y = f(x) y\=f(x) 代码演示:将String
类型转换为Integer
类型。
public class FuntionTest {
public static void main(String[] args) {
parse(s -> Integer.parseInt(s), "10");
}
static void parse(Function<String, Integer> func , String str) {
int num = func.apply(str);
System.out.println(num);
}
}
默认方法:compose,源码:
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
compose方法接收一个Function类型的参数,先用传入的Function对象执行对应的apply方法,然后使用当前的Function对象的执行对应的apply方法
代码演示:先将字符串与字符’0’拼接,再转为整数
public class FuntionTest {
public static void main(String[] args) {
parse(s1 -> Integer.parseInt(s1), s2 -> s2 + '0', "10");
}
static void parse(Function<String, Integer> func1, Function<String, String> func2 , String str) {
//先用传入的 func2 = s2 -> s2 + '0'; 执行apply
//然后用当前的 func1 = s1 -> Integer.parseInt(s1); 执行apply
int num = func1.compose(func2).apply(str);
System.out.println(num);
}
}
默认方法:andThen,源码:
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
andThen方法接收一个Function类型的参数,先用当前的Function对象的执行对应的apply方法,然后使用传入的Function对象执行对应的apply方法
代码演示:先将字符串转为整数,然后将此整数乘以10
public class FuntionTest {
public static void main(String[] args) {
parse(s -> Integer.parseInt(s), i -> i * 12, "10");
}
static void parse(Function<String, Integer> func1, Function<Integer, Integer> func2 , String str) {
//先用当前的 func1 = s1 -> Integer.parseInt(s1); 执行apply
//然后用传入的 func2 = i -> i * 12; 执行apply
int num = func1.andThen(func2).apply(str);
System.out.println(num);
}
}
- 感谢你赐予我前进的力量