@FunctionalInterface从1.8开始就可以使用了, 函数式接口,都有哪些规则呢,今天实践总结一下@FunctionalInterface的使用都有哪些方式和规则
特点
先说特点
- 唯一抽象方法:被
@FunctionalInterface
注解的接口必须保证只包含一个未实现的方法。如果接口中有多于一个抽象方法,编译器将会报错。 - Lambda表达式的载体:函数式接口是Java中Lambda表达式的目标类型。你可以通过Lambda表达式来创建该接口的实例,使得代码更加简洁和易于阅读。
- 默认方法和静态方法不受影响:虽然函数式接口只能有一个抽象方法,但是它可以包含任意数量的默认方法(default method)和静态方法。这些方法不会影响接口成为函数式接口的性质。
- 简化类型检查:编译器会在编译期检查带有
@FunctionalInterface
注解的接口,确保它满足函数式接口的条件。这样可以避免开发者无意中在接口中添加额外的抽象方法,导致Lambda表达式无法正常绑定。 - 面向函数式编程的支持:
@FunctionalInterface
注解是Java 8为了支持函数式编程风格而引入的关键特性之一,它使得Java能够更好地融入函数式编程的世界,让开发者可以写出更加简洁、更具表达力的代码。
好了进入主题,开发离不开编码,实践是检验真理的唯一标准,demo来了
我先定义一个@FunctionalInterface注解的函数式接口
一般我把重点规则都注释到代码中了,大家注意看,看我良好的注释习惯哈哈
import com.mtgg.laoxiang.common.response.Result;
/**
* 定义函数式接口
*/
@FunctionalInterface
public interface FunctionFruit {
Result execute(String s);
/**
* 可定义变量,必须要有默认值
*/
public static final String NAME = "变量name";
/**
* 可定义静态方法
*/
static void drink() {
System.out.println("可定义静态方法");
}
/**
* 可定义默认方法
*/
default void eat() {
System.out.println("可定义默认方法");
}
}
虽然用@FunctionalInterface注解,别忘了它本身是一个接口,可以被其他接口继承扩展
public interface IFunctionAnimal extends FunctionFruit {
int apply();
int dog();
}
然后可以有一个实现类,这个实现类会将FunctionFruit和IFunctionAnimal的接口都实现掉
/**
* 有函数式的接口可以被实现使用
*/
@Component
public class IBirdImpl implements IFunctionAnimal {
@Override
public Result execute(String r) {
System.out.println("bird 1");
return Result.success("bird 1");
}
@Override
public int apply() {
System.out.println("bird 2");
return 0;
}
@Override
public int dog() {
System.out.println("bird 3");
return 0;
}
}
然后我们定义一个Animal对象,这个对象我依赖了好几个函数式接口,包括官方的Function,我们自定义的FunctionFruit和IFunctionAnimal,说明我们的动作可以在执行前用对象传递,什么时候执行你说了算,什么时候执行我们往下看
import com.mtgg.laoxiang.common.response.Result;
import lombok.Getter;
import lombok.Setter;
import java.util.function.Function;
@Getter
@Setter
public class Animal{
//现有函数是接口 作为属性 String作为入参,Result作为自定义返回封装类
private Function<String, Result> function;
//自定义函数式接口
private FunctionFruit functionFruit;
private FunctionFruit functionFruit2;
private FunctionFruit functionFruit3;
private FunctionFruit functionFruit4;
private IFunctionAnimal IFunctionAnimal;
}
Result就是自己封装的一个通用返回类,有errorInfo,errorMsg,data
下面就是如何使用,先去看一遍代码,然后我再解释
import com.mtgg.laoxiang.common.response.Result;
import java.util.function.Function;
/**
* 函数式接口实践
* 一般函数式接口本身可以作为方法的参数(通过匿名内部类的方式实现);可以作为对象的属性;可以注入到全局map中;
* 可以被集成然后注入使用;
* {@link java.util.function.Supplier}
* {@link java.util.function.Predicate}
* {@link java.util.function.Consumer}
* {@link java.util.function.Function}
*/
public class FTest {
public static void main(String[] args) {
FTest fTest = new FTest();
Animal animal = fTest.buildAnimal();
fTest.example(animal);
}
public Animal buildAnimal() {
Animal animal = new Animal();
animal.setIFunctionAnimal(new IBirdImpl());
//封装的几个函数接口
animal.setFunction(new Function<String, Result>() {
@Override
public Result apply(String s) {
System.out.println("Function--->" + s);
return Result.success();
}
});
//TODO 写法1 匿名内部类
animal.setFunctionFruit(new FunctionFruit() {
@Override
public Result execute(String r) {
System.out.println("写法1 匿名内部类-->" + r);
return Result.success();
}
});
//TODO 写法2 方法引用 fruitTest必须为静态方法
FunctionFruit functionFruit = FTest::fruitTest;
animal.setFunctionFruit2(functionFruit);
//TODO 写法3 实例方法引用 可不为静态方法
FTest fTest = new FTest();
FunctionFruit f3 = fTest::test2;
animal.setFunctionFruit3(f3);
//TODO 写法4
FunctionFruit f4 = x -> Result.success();
animal.setFunctionFruit4(f4);
return animal;
}
public void example(Animal animal) {
Result apply = animal.getFunction().apply("a");
//可通过下级接口调用父级函数式接口
Result cResult = animal.getIFunctionAnimal().execute("b");
animal.getIFunctionAnimal().eat();
animal.getIFunctionAnimal().apply();
System.out.println(FunctionFruit.NAME);
FunctionFruit.drink();
animal.getFunctionFruit().execute("c");
animal.getFunctionFruit2().execute("d");
animal.getFunctionFruit3().execute("e");
animal.getFunctionFruit4().execute("f");
}
public Result test2(String type) {
System.out.println("写法3 实例方法引用-->" + type);
return Result.success();
}
private static Result fruitTest(String type) {
//可自定义逻辑……
System.out.println("写法2 方法引用-->" + type);
return Result.success();
}
}
代码看完了,可以看出实际上我们执行是在example方法中,Animal对象作为上下文对象,我们写一个buildAnimal构建方法用来构建Animal对象,Animal定义那么多函数式接口是因为有不同的写法,都返回待执行的函数,可以让大家看到不同的写法的执行情况
函数式定义写法
写法一:匿名内部类
FunctionFruit f = new FunctionFruit() {
@Override
public Result execute(String r) {
System.out.println("写法1 匿名内部类-->" + r);
return Result.success();
}
}
写法二:方法引用,大家可以看到有个限制,就是fruitTest必须为静态方法
FunctionFruit functionFruit = FTest::fruitTest;
private static Result fruitTest(String type) {
//可自定义逻辑……
System.out.println("写法2 方法引用-->" + type);
return Result.success();
}
写法三:实例方法引用,可以看到这种方法解决了写法二必须为静态方法的问题
FTest fTest = new FTest();
FunctionFruit f3 = fTest::test2;
public Result test2(String type) {
System.out.println("写法3 实例方法引用-->" + type);
return Result.success();
}
写法四:简单函数直接定义,这种方式比较简单,所以直接定义可以用这种,比如a+b等等
FunctionFruit f4 = x -> Result.success();
example方法中是我们的调用,上面我们调用的各种地方的方法都有实践调用到,打印结果在这
Function—>a
bird 1
可定义默认方法
bird 2
变量name
可定义静态方法
写法1 匿名内部类–>c
写法2 方法引用–>d
写法3 实例方法引用–>e
在实际业务中大家可以斟酌使用,还是很有用的,逼格也挺高
大家可以实践一下,祝我们都能够写出漂亮的,健壮的,可扩展的,绝绝子的代码,see you later