网站首页 > 基础教程 正文
内置函数式接口来由来
我们知道使用Lambda表达式的前提是需要有函数式接口。而Lambda使用时不关心接口名,抽象方法名,只关心抽 象方法的参数列表和返回值类型。因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口。
函数式接口在Java中是指:有且仅有一个抽象方法的接口。
import java.util.List;
public class Demo01UserFunctionalInterface {
public static void main(String[] args) {
// 调用函数式接口中的方法
method((arr) -> {
int sum = 0;
for (int n : arr) {
sum += n;
}
return sum;
});
}
// 使用自定义的函数式接口作为方法参数
public static void method(Operator op) {
int[] arr = {1, 2, 3, 4};
int sum = op.getSum(arr);
System.out.println("sum = " + sum);
}
}
@FunctionalInterface
interface Operator {
public abstract int getSum(int[] arr);
}
常用内置函数式接口介绍
它们主要在 java.util.function 包中。下面是最常用的几个接口。
- Supplier接口@FunctionalInterface
public interface Supplier<T> {
public abstract T get();
} - Consumer接口@FunctionalInterface
public interface Consumer<T> {
public abstract void accept(T t);
} - Function接口@FunctionalInterface
public interface Function<T, R> {
public abstract R apply(T t);
} - Predicate接口
@FunctionalInterface
public interface Predicate<T> {
public abstract boolean test(T t);
}
Predicate接口用于做判断,返回boolean类型的值
Supplier接口
java.util.function.Supplier 接口,它意味着"供给" , 对应的Lambda表达式需要“对外提供”一个符合泛型类 型的对象数据。
@FunctionalInterface
public interface Supplier<T> {
public abstract T get();
}
使用Lambda表达式返回数组元素最大值
使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。提示:接口的泛型请使用 java.lang.Integer 类。
代码示例:
public class Demo05Supplier {
public static void main(String[] args) {
printMax(() -> {
int[] arr = {10, 20, 100, 30, 40, 50};
// 先排序,最后就是最大的
Arrays.sort(arr);
return arr[arr.length - 1]; // 最后就是最大的
});
}
private static void printMax(Supplier<Integer> supplier) {
int max = supplier.get();
System.out.println("max = " + max);
}
}
Consumer接口
java.util.function.Consumer 接口则正好相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛 型参数决定。
@FunctionalInterface
public interface Consumer<T> {
public abstract void accept(T t);
}
使用Lambda表达式将一个字符串转成大写和小写的字符串
Consumer消费型接口,可以拿到accept方法参数传递过来的数据进行处理, 有参无返回的接口。基本使用如:
public class Demo06Consumer {
public static void main(String[] args) {
// Lambda表达式
test((String s) -> {
System.out.println(s.toLowerCase());
});
}
public static void test(Consumer<String> consumer) {
consumer.accept("HelloWorld");
}
}
默认方法:andThen
如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费一个数据的时候,首先做一个操 作,然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。下面是JDK的源代 码:
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
[备注: java.util.Objects 的 requireNonNull 静态方法将会在参数为null时主动抛出]
[NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。]
要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。例如两个步骤组合 的情况:
public class Demo07ConsumerAndThen {
public static void main(String[] args) {
// Lambda表达式
test((String s) -> {
System.out.println(s.toLowerCase());
}, (String s) -> {
System.out.println(s.toUpperCase());
});
// Lambda表达式简写
test(s -> System.out.println(s.toLowerCase()), s ->
System.out.println(s.toUpperCase()));
}
public static void test(Consumer<String> c1, Consumer<String > c2) {
String str = "Hello World";
// c1.accept(str); // 转小写
// c2.accept(str); // 转大写
// c1.andThen(c2).accept(str);
c2.andThen(c1).accept(str);
}
}
运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组 合。
Function接口
java.util.function.Function 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。有参数有返回值。
@FunctionalInterface
public interface Function<T, R> {
public abstract R apply(T t);
}
使用Lambda表达式将字符串转成数字
Function转换型接口,对apply方法传入的T类型数据进行处理,返回R类型的结果,有参有返回的接口。使用的场景 例如:将 String 类型转换为 Integer 类型。
public class Demo08Function {
public static void main(String[] args) {
// Lambda表达式
test((String s) -> {
return Integer.parseInt(s); // 10
});
}
public static void test(Function<String, Integer> function) {
Integer in = function.apply("10");
System.out.println("in: " + (in + 5));
}
}
默认方法:andThen
Function 接口中有一个默认的 andThen 方法,用来进行组合操作。JDK源代码如:
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
该方法同样用于“先做什么,再做什么”的场景,和 Consumer 中的 andThen 差不多:
public class Demo09FunctionAndThen {
public static void main(String[] args) {
// Lambda表达式
test((String s) -> {
return Integer.parseInt(s);
}, (Integer i) -> {
return i * 10;
});
}
public static void test(Function<String, Integer> f1, Function<Integer, Integer> f2) {
// Integer in = f1.apply("66"); // 将字符串解析成为int数字
// Integer in2 = f2.apply(in);// 将上一步的int数字乘以10
Integer in3 = f1.andThen(f2).apply("66");
System.out.println("in3: " + in3); // 660
}
}
第一个操作是将字符串解析成为int数字,第二个操作是乘以10。两个操作通过 andThen 按照前后顺序组合到了一 起。
[请注意,Function的前置条件泛型和后置条件泛型可以相同。]
Predicate接口
有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用 java.util.function.Predicate 接口。
@FunctionalInterface
public interface Predicate<T> {
public abstract boolean test(T t);
}
Predicate接口用于做判断,返回boolean类型的值
使用Lambda判断一个人名如果超过3个字就认为是很长的名字
对test方法的参数T进行判断,返回boolean类型的结果。用于条件判断的场景:
public class Demo10Predicate {
public static void main(String[] args) {
test(s -> s.length() > 3, "迪丽热巴");
}
private static void test(Predicate<String> predicate, String str) {
boolean veryLong = predicate.test(str);
System.out.println("名字很长吗:" + veryLong);
}
}
条件判断的标准是传入的Lambda表达式逻辑,只要名称长度大于3则认为很长。
默认方法:and
是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实 现“并且”的效果时,可以使用default方法 and 。其JDK源码为:
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
使用Lambda表达式判断一个字符串中即包含W,也包含H
使用Lambda表达式判断一个字符串中包含W或者包含H
使用Lambda表达式判断一个字符串中即不包含W
如果要判断一个字符串既要包含大写“H”,又要包含大写“W”,那么:
public class Demo10Predicate_And_Or_Negate {
public static void main(String[] args) {
// Lambda表达式
test((String s) -> {
return s.contains("H");
}, (String s) -> {
return s.contains("W");
});
}
public static void test(Predicate<String> p1, Predicate<String> p2) {
String str = "HelloWorld";
boolean b1 = p1.test(str); // 判断包含大写“H”
boolean b2 = p2.test(str); // 判断包含大写“W”
// if (b1 && b2) {
// System.out.println("即包含W,也包含H");
// }
boolean bb = p1.and(p2).test(str);
if (bb) {
System.out.println("即包含W,也包含H");
}
}
}
默认方法:or
使用Lambda表达式判断一个字符串中包含W或者包含H
与 and 的“与”类似,默认方法 or 实现逻辑关系中的“或”。JDK源码为:
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
如果希望实现逻辑“字符串包含大写H或者包含大写W”,那么代码只需要将“and”修改为“or”名称即可,其他都不变:
public class Demo10Predicate_And_Or_Negate {
public static void main(String[] args) {
// Lambda表达式
test((String s) -> {
return s.contains("H");
}, (String s) -> {
return s.contains("W");
});
}
public static void test(Predicate<String> p1, Predicate<String> p2) {
String str = "HelloWorld";
boolean b1 = p1.test(str); // 判断包含大写“H”
boolean b2 = p2.test(str); // 判断包含大写“W”
// if (b1 || b2) {
// System.out.println("有H,或者W");
// }
boolean bbb = p1.or(p2).test(str);
if (bbb) {
System.out.println("有H,或者W");
}
}
}
默认方法:negate
使用Lambda表达式判断一个字符串中即不包含W
“与”、“或”已经了解了,剩下的“非”(取反)也会简单。默认方法 negate 的JDK源代码为:
default Predicate<T> negate() {
return (t) -> !test(t);
}
从实现中很容易看出,它是执行了test方法之后,对结果boolean值进行“!”取反而已。一定要在 test 方法调用之前调 用 negate 方法,正如 and 和 or 方法一样:
import java.util.function.Predicate;
public class Demo10Predicate_And_Or_Negate {
public static void main(String[] args) {
// Lambda表达式
test((String s) -> {
return s.contains("H");
}, (String s) -> {
return s.contains("W");
});
}
public static void test(Predicate<String> p1, Predicate<String> p2) {
String str = "HelloWorld";
boolean b1 = p1.test(str); // 判断包含大写“H”
boolean b2 = p2.test(str); // 判断包含大写“W”
// 没有H,就打印
// if (!b1) {
// System.out.println("没有H");
// }
boolean test = p1.negate().test(str);
if (test) {
System.out.println("没有H");
}
}
}
小结
- Supplier接口@FunctionalInterface
public interface Supplier<T> {
public abstract T get();
} - Consumer接口@FunctionalInterface
public interface Consumer<T> {
public abstract void accept(T t);
} - Consumer接口@FunctionalInterface
public interface Function<T, R> {
public abstract R apply(T t);
} - Predicate接口@FunctionalInterface
public interface Predicate<T> {
public abstract boolean test(T t);
}
Predicate接口用于做判断,返回boolean类型的值
猜你喜欢
- 2024-11-30 Java开发「函数式编程」——Lambda表达式
- 2024-11-30 玩转java8 lambda表达式三之对象方法引用
- 2024-11-30 Java 中的 Supplier:让数据生成更灵活
- 2024-11-30 Java 中的 Predicate:让判断逻辑更清晰灵活
- 2024-11-30 Java8精华-函数式编程(一)读完这篇,你将彻底理解
- 2024-11-30 你知道实现一个JAVA接口有几种方式吗?配合lambda更加意想不到
- 2024-11-30 java8精华-函数式编程-Predicate(四)
- 2024-11-30 巧妙应用 Java 的 Functional Interface:让代码更简洁、更灵活
- 2024-11-30 函数式接口编程真没那么难,简单几行让你的代码更优雅
- 2024-11-30 初窥函数式接口,不会取标题,没有噱头,全是干货
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)