函数式接口本质上是一个具有单个抽象方法的接口,在Java中通常用@FunctionalInterface
注解来标识。这个抽象方法定义了该接口的输入参数和输出类型,但不包含具体的实现。具体的实现会在使用这个函数式接口时提供,例如通过lambda表达式或方法引用来实现。简而言之,函数式接口关注的是方法的签名,而不是其具体的实现。
通过函数式接口的定义,我们可以利用lambda表达式来传递行为,而不需要显式地定义一个实现类。这种方式极大地简化了代码,并且提升了灵活性和可读性。
函数式接口的定义
以下是一个简单的函数式接口示例:
@FunctionalInterface
public interface MyFunction {
void apply(int value);
}
这个接口定义了一个接受一个整数参数且没有返回值的方法apply
。该接口没有提供具体的实现。
使用函数式接口
使用函数式接口时,可以通过lambda表达式或方法引用来提供具体的实现。
public class Main {
public static void main(String[] args) {
// 使用lambda表达式实现函数式接口
MyFunction printFunction = (value) -> System.out.println("Value: " + value);
// 调用接口的方法
printFunction.apply(5);
// 使用方法引用实现函数式接口
MyFunction anotherPrintFunction = System.out::println;
// 调用接口的方法
anotherPrintFunction.apply(10);
}
}
在这个示例中,printFunction
和anotherPrintFunction
都是MyFunction
接口的实现,前者通过lambda表达式实现,后者通过方法引用实现。
具体实现与函数式接口的关系
函数式接口本身不包含任何具体的实现,它只是定义了一个方法的签名。在使用这个接口时,才会提供具体的实现。因此,函数式接口提供了一个标准的接口,让我们可以灵活地提供各种不同的实现。
例子
1、导出数据到CSV和JSON
通过函数式接口的概念,我们可以设计灵活的数据导出功能。例如,假设我们有一个函数式接口DataExporter
,它用于定义数据导出的行为:
@FunctionalInterface
public interface DataExporter<T> {
void export(List<T> data, String filePath) throws IOException;
}
然后,我们可以创建不同的实现来导出数据到CSV和JSON:
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
public class DataExporterDemo {
public static void main(String[] args) throws IOException {
// CSV导出实现
DataExporter<String> csvExporter = (data, filePath) -> {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
for (String item : data) {
writer.write(item);
writer.newLine();
}
}
};
// JSON导出实现
DataExporter<Object> jsonExporter = (data, filePath) -> {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(new File(filePath), data);
};
// 示例数据
List<String> stringList = List.of("Alice", "Bob", "Charlie");
List<Integer> intList = List.of(1, 2, 3, 4, 5);
// 导出数据
String csvFilePath = "data.csv";
String jsonFilePath = "data.json";
csvExporter.export(stringList, csvFilePath);
jsonExporter.export(intList, jsonFilePath);
}
}
在这个示例中,DataExporter
接口定义了导出数据的方法签名,而具体的实现是在主类中通过lambda表达式提供的。
2、利用函数式接口+枚举实现工厂模式
定义接口
@FunctionalInterface
public interface ICalculate<T1, T2> {
void doCalculate(T1 t1, T2 t2);
}
定义具体实现
import java.util.List;
public class CalculateService {
/**
* 说明:最小值计算
*
*/
public static void minCalculate(List<Vo1> voList, Vo2 vo) {
// 计算逻辑
}
/**
* 说明:最大值计算计算
*
*/
public static void maxPriceCalculate(List<Vo3> voList, Vo4 vo) {
// 计算逻辑
}
}
定义枚举类
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
import java.util.stream.Stream;
@Getter
@AllArgsConstructor
public enum CalculateEnum {
/**
* 最小值
*/
MIN_PRICE("min", (List<Vo1> list, Vo2 vo2) -> CalculateService.minCalculate(list, vo2)),
/**
* 最大值
*/
MAX_PRICE("max", (List<Vo3> list, Vo4 vo4) -> CalculateService.maxCalculate(list, vo4));
private final String code;
private final ICalculate<?, ?> calculate;
/**
* 通过code获取Enum
*
* @param code code
* @return CalculateEnum
*/
public static CalculateEnum getByCode(String code) {
return Stream.of(values())
.filter(e -> e.getCode().equals(code))
.findFirst()
.orElse(null);
}
@SuppressWarnings("unchecked")
public <T1, T2> void doCalculate(T1 t1, T2 t2) {
try {
((ICalculate<T1, T2>) calculate).doCalculate(t1, t2);
} catch (Exception e) {
throw new RuntimeException("计算异常", e);
}
}
}
具体应用
import java.util.List;
public class Main {
public static void main(String[] args) {
// 示例数据
List<Vo1> vo1List= List.of(new Vo1());
Vo2 vo2= new Vo2();
List<Vo3> vo3List= List.of(new Vo3());
Vo4 vo4= new Vo4();
// 使用最小值计算
CalculateEnum minEnum = CalculateEnum.getByCode("min");
if (minEnum != null) {
minEnum.doCalculate(vo1List, vo2);
} else {
System.out.println("未找到对应的计算规则");
}
// 使用最大值计算
CalculateEnum minEnum = CalculateEnum.getByCode("max");
if (minEnum != null) {
minEnum.doCalculate(vo3List, vo4);
} else {
System.out.println("未找到对应的计算规则");
}
}
}
总结
函数式接口关注的是方法的签名,定义了方法的输入和输出,而具体的实现由使用者通过lambda表达式或方法引用来提供。这使得代码更加简洁和灵活,可以在不同的场景下轻松地提供不同的实现逻辑。