【jdk8新特性】lambda表达式函数式接口
什么是函数式接口
函数式接口在Java中是指:有且仅有一个抽象方法的接口。
为什么需要函数式接口
因为写lambda表达式的前提是需要参数或者变量为函数式接口
自定义函数接口
例子:
public class Demo01UserFunctionalInterface {
public static void main(String[] args) {
test((int a, int b) -> {
System.out.println(a + b);
});
}
public static void test(Operation op) {
op.getSum(1, 2);
}
}
interface Operation {
public abstract void getSum(int a, int b);
}
结果
3
这个例子中Operation就是函数式接口
常用的内置函数接口
Supplier
源代码
@FunctionalInterface
public interface Supplier<T> {
T get();
}
可以看出这个函数式接口的抽象方法是get,且不需要参数,所以这个Supplier接口的作用是 无需传参处理数据获得返回值,它意味着"供给" , 对应的Lambda表达式需要“对外提供”一个符合泛型类 型的对象数据。
例子
public class Demo02Supplier {
// 使用Lambda表达式返回数组元素最大值
public static void main(String[] args) {
System.out.println("开始了");
printMax(() -> {
int[] arr = {11, 99, 88, 77, 22};
Arrays.sort(arr); // 升序排序
return arr[arr.length - 1];
});
}
public static void printMax(Supplier<Integer> supplier) {
System.out.println("aa");
int max = supplier.get();
System.out.println("max = " + max);
}
}
结果
开始了
aa
max = 99
可以看出 printMax方法的参数类型是 Supplier函数式接口 所以我们可以使用lambda表达式,我们重写了 Supplier的get方法 内容是一个链表的升序排序 并返回最大值
Consumer
源代码
@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); };
}
}
可以看出 Consumer函数式接口 只有一个抽象方法accept(T t) 以及一个 默认方法andThen(Consumer),accept方法需要一个参数并且没有返回值,它不是生产一个数据,而是消费一个数据,其数据类型由泛 型参数决定。
例子
public class Demo03Consumer {
// 使用Lambda表达式将一个字符串转成大写的字符串
public static void main(String[] args) {
System.out.println("开始啦");
printHello((String str) -> {
System.out.println(str.toUpperCase());
});
}
public static void printHello(Consumer<String> consumer) {
System.out.println("aaa");
consumer.accept("Hello World");
}
}
可以看出 printHello方法的参数类型是 Consumer函数式接口 所以我们可以使用lambda表达式,我们重写了 Supplier的accept方法 内容是一个链表的升序排序 并返回最大值
默认方法:andThen
如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。
例子
public class Demo04ConsumerAndThen {
// 使用Lambda表达式先将一个字符串转成小写的字符串,再转成大写
public static void main(String[] args) {
System.out.println("开始啦");
printHello((String str) -> {
System.out.println(str.toLowerCase());
}, (String str) -> {
System.out.println(str.toUpperCase());
});
}
public static void printHello(Consumer<String> c1, Consumer<String> c2) {
System.out.println("aa");
String str = "Hello World";
// c1.accept(str);
// c2.accept(str);
c2.andThen(c1).accept(str);
}
}
结果
aa
HELLO WORLD
hello world
可以看出运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组合。
Function
源代码
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;
}
}
可以看出 Function函数式接口 只有一个抽象方法R apply(T t),需要参数和返回值,用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。有参数有返回值。
例子
public class Demo05Function {
// 使用Lambda表达式将字符串转成数字
public static void main(String[] args) {
System.out.println("开始");
getNumber((String str) -> {
int i = Integer.parseInt(str);
return i;
});
}
public static void getNumber(Function<String, Integer> function) {
System.out.println("aa");
Integer num1 = function.apply("10");
System.out.println("num1 = " + num1);
}
}
结果
开始
aa
num1 = 10
可以看出 Function接口的apply方法 对字符串10 进行处理 返回整型10
Predicate接口
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);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
@SuppressWarnings("unchecked")
static <T> Predicate<T> not(Predicate<? super T> target) {
Objects.requireNonNull(target);
return (Predicate<T>)target.negate();
}
}
可以看出 Predicate函数式接口 只有一个抽象方法 boolean test(T t);需要一个参数并返回boolean类型,有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果,这时可以使用Predicate接口。
例子
public class Demo07Predicate {
// 使用Lambda判断一个人名如果超过3个字就认为是很长的名字
public static void main(String[] args) {
System.out.println("开始啦");
isLongName((String name) -> {
return name.length() > 3;
});
}
public static void isLongName(Predicate<String> predicate) {
System.out.println("aa");
boolean isLong = predicate.test("迪丽热巴");
System.out.println("是否是长名字: " + isLong);
}
}
结果
开始啦
aa
是否是长名字: true
可以看出传入一个长度为4的字符串 test方法里进行判断是否 长度>3 返回true
默认方法:and,or,negate
解释
and:将两个 Predicate 条件使用“与”逻辑连接起来实 现“并且”的效果时,可以使用default方法 and 。
or:将两个 Predicate 条件使用“与”逻辑连接起来实 现“或”的效果时,可以使用default方法 or。
negate:Predicate 中的"取反,非"。
例子
// 使用Lambda表达式判断一个字符串中即包含W,也包含H
// 使用Lambda表达式判断一个字符串中包含W或者包含H
// 使用Lambda表达式判断一个字符串中不包含W
public static void main(String[] args) {
test((String str) -> {
// 判断是否包含W
return str.contains("W");
}, (String str) -> {
// 判断是否包含H
return str.contains("H");
});
}
public static void test(Predicate<String> p1, Predicate<String> p2) {
// String str = "Hello orld";
// boolean b1 = p1.test(str);
// boolean b2 = p2.test(str);
// if (b1 && b2) {
// System.out.println("即包含W,也包含H");
// }
// 使用Lambda表达式判断一个字符串中即包含W,也包含H
String str = "Hello World";
boolean b = p1.and(p2).test(str);
if (b) {
System.out.println("即包含W,也包含H");
}
// 使用Lambda表达式判断一个字符串中包含W或者包含H
boolean b1 = p1.or(p2).test(str);
if (b1) {
System.out.println("包含W或者包含H");
}
// 使用Lambda表达式判断一个字符串中不包含W
boolean b2 = p1.negate().test("Hello W");
// negate相当于取反 !boolean
if (b2) {
System.out.println("不包含W");
}
}
结果
即包含W,也包含H
包含W或者包含H