1、什么是函数式接口
- 只包含一个抽象方法的接口,称为
函数式接口
。 - 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
- 我们可以在任意函数式接口上使用
@FunctionalInterface
注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
2、自定义函数式接口,并作为参数传递 Lambda 表达式
java
import java.util.function.Function;
// 函数式接口中使用泛型
@FunctionalInterface
public interface MyFunc<T> {
T getValue(T t);
}
public class Main {
// 作为参数传递 Lambda 表达式
public static String toUpperString(MyFunc<String> mf, String str) {
return mf.getValue(str);
}
public static void main(String[] args) {
String newStr = toUpperString((str) -> str.toLowerCase(), "ABC");
System.out.println(newStr); // 输出: abc
}
}
为什么可以传入 lambda 表达式 (str) -> str.toLowerCase() ??
答:
MyFunc<String>
接口定义了一个抽象方法 T getValue(T t)
,该方法接受一个泛型参数并返回一个泛型结果。Lambda 表达式 (str) -> str.toLowerCase()
的签名也符合这个描述符,因为它接受一个字符串参数并返回一个字符串结果。
Lambda 表达式的类型是根据上下文推断出来的。在这种情况下,编译器可以根据 toUpperString
方法的参数类型 MyFunc<String>
推断出 Lambda 表达式的类型,并且该 Lambda 表达式的参数类型和返回类型都符合 MyFunc<String>
接口的方法签名。
因此,可以将 Lambda 表达式 (str) -> str.toLowerCase()
传递给 toUpperString
方法作为参数,编译器会将其视为 MyFunc<String>
类型的实例。
3、Java 内置四大核心函数式接口
函数式接口 参数类型 返回类型 用途
Consumer<T> 消费型接口 | T | void | 对类型为T的对象应用操作,包含方法: void accept(T t) |
Supplier<T> 供给型接口 | 无 | T | 返回类型为T的对象,包含方法:T get(); |
Function<T, R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t); |
Predicate<T> 断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean 值。包含方法boolean test(T t); |
4、其他接口
函数式接口 参数类型 返回类型 用途
BiFunction<T, U, R> | T, U | R | 对类型为 T, U 参数应用操作,返回 R 类型的结果。包含方法为 R apply(T t, U u); |
UnaryOperator<T> (Function子接口) | T | T | 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为 T apply(T t); |
BinaryOperator<T> (BiFunction 子接口) | T, T | T | 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为 T apply(T t1, T t2); |
BiConsumer<T, U> | T, U | void | 对类型为T, U 参数应用操作。包含方法为 void accept(T t, U u) |
ToIntFunction<T> ToLongFunction<T> ToDoubleFunction<T> | T | int long double | 分别计算 int 、 long 、double、值的函数 |
IntFunction<R> LongFunction<R> DoubleFunction<R> | int long double | R | 参数分别为int、long、double 类型的函数 |
5、Consumer<T>
消费型接口
- 对类型为T的对象应用操作,包含方法:void accept(T t)
import java.util.function.Consumer;
public class Main {
// 接受一个Consumer作为参数
public static void printMessage(Consumer<String> consumer, String message) {
consumer.accept(message);
}
public static void main(String[] args) {
// 使用printMessage方法传入Consumer
printMessage(str -> System.out.println(str), "Hello, world!");
}
}
6、Supplier<T> 供给型接口
- 返回类型为T的对象,包含方法:T get();
import java.util.function.Supplier;
public class Main {
// 接受一个Supplier作为参数
public static void generateAndPrint(Supplier<Integer> supplier) {
int randomNumber = supplier.get();
System.out.println("Random number: " + randomNumber);
}
public static void main(String[] args) {
// 使用generateAndPrint方法传入Supplier
generateAndPrint(() -> (int) (Math.random() * 100));
}
}
7、Function<T, R> 函数型接口
- 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t);
import java.util.function.Function;
public class Main {
// 接受一个Function作为参数
public static void processString(Function<String, String> function, String input) {
String result = function.apply(input);
System.out.println("Processed string: " + result);
}
public static void main(String[] args) {
// 使用processString方法传入Function
processString(str -> str.toUpperCase(), "hello");
}
}
8、Predicate<T> 断言型接口
- 确定类型为T的对象是否满足某约束,并返回boolean 值。包含方法boolean test(T t);
import java.util.function.Predicate;
public class Main {
// 接受一个Predicate作为参数
public static void checkPredicate(Predicate<Integer> predicate, int num) {
boolean result = predicate.test(num);
System.out.println(num + " is even? " + result);
}
public static void main(String[] args) {
// 使用checkPredicate方法传入Predicate
checkPredicate(num -> num % 2 == 0, 4);
}
}
9、方法引用和构造器引用
1、方法引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)方法引用:使用操作符 ::
将方法名和对象或类的名字分隔开来。 如下三种主要使用情况:
1. 静态方法引用
静态方法引用是指引用静态方法的情况。
类::静态方法
java
import java.util.function.Supplier;
public class Main {
// 静态方法
public static String getMessage() {
return "Hello, world!";
}
public static void main(String[] args) {
// 使用方法引用来引用静态方法
Supplier<String> supplier = Main::getMessage;
String message = supplier.get();
System.out.println(message); // 输出: Hello, world!
}
}
在这个示例中,getMessage
方法被引用为静态方法Main::getMessage
,然后将这个引用赋值给了一个Supplier
接口。当调用supplier.get()
时,实际上调用了Main.getMessage()
方法。
2.实例方法引用
实例方法引用是指引用对象的实例方法的情况。
对象::实例方法
public void test2(){
Employee emp = new Employee(101, "张三", 18, 9999.99);
Supplier<String> sup = () -> emp.getName();
System.out.println(sup.get());
System.out.println("----------------------------------");
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
import java.util.function.Consumer;
class Printer {
// 实例方法
public void printMessage(String message) {
System.out.println(message);
}
}
public class Main {
public static void main(String[] args) {
// 创建Printer对象
Printer printer = new Printer();
// 使用方法引用来引用实例方法
Consumer<String> consumer = printer::printMessage;
consumer.accept("Hello, world!"); // 输出: Hello, world!
}
}
类::实例方法
public void test5(){
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
System.out.println(bp.test("abcde", "abcde"));
System.out.println("-----------------------------------------");
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("abc", "abc"));
System.out.println("-----------------------------------------");
Function<Employee, String> fun = (e) -> e.show();
System.out.println(fun.apply(new Employee()));
System.out.println("-----------------------------------------");
Function<Employee, String> fun2 = Employee::show;
System.out.println(fun2.apply(new Employee()));
}
2、构造器引用
构造器引用允许你像调用方法一样调用构造器来创建对象。语法为ClassName::new
,其中ClassName
是要使用的构造器所属的类名
@FunctionalInterface
interface MyInterface {
MyClass create(String s);
}
class MyClass {
private String value;
public MyClass(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
public class Main {
public static void main(String[] args) {
// 使用构造器引用创建对象
MyInterface myInterface = MyClass::new;
MyClass obj = myInterface.create("Hello");
System.out.println(obj.getValue()); // 输出: Hello
}
}
与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!
public void test6(){
Supplier<Employee> sup = () -> new Employee();
System.out.println(sup.get());
System.out.println("------------------------------------");
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
}
public void test7(){
Function<String, Employee> fun = Employee::new;
BiFunction<String, Integer, Employee> fun2 = Employee::new;
}
简单理解可以ClassName::new获取函数式接口对象
3、
数组引用
数组构造器引用允许你创建数组实例。语法为TypeName[]::new
,其中TypeName
是数组元素的类型。
@FunctionalInterface
interface ArrayCreator {
String[] create(int length);
}
public class Main {
public static void main(String[] args) {
// 使用数组构造器引用创建数组
ArrayCreator arrayCreator = String[]::new;
String[] array = arrayCreator.create(5);
System.out.println(array.length); // 输出: 5
}
}
public void test8(){
Function<Integer, String[]> fun = (args) -> new String[args];
String[] strs = fun.apply(10);
System.out.println(strs.length);
System.out.println("--------------------------");
Function<Integer, Employee[]> fun2 = Employee[] :: new;
Employee[] emps = fun2.apply(20);
System.out.println(emps.length);
}