java函数式 new_java的函数式接口有什么意义?

主要内容:函数式接口是什么

函数式接口玩法介绍

使用场景1

使用场景2

函数式接口是什么

定义:有且仅有一个抽象方法的接口(不包括默认方法、静态方法以及对Object方法的重写)

大家对函数式接口的认识应该都来自于Java8的Stream API,比如Predicate、Function,借助这些函数式接口,Stream才能写出一个个骚操作:

public class StreamTest {

public static void main(String[] args) {

List userList = Lists.newArrayList();

userList.add(new User(1L, "彼得", 18));

userList.add(new User(2L, "鲍勃", 19));

userList.add(new User(3L, "威廉", 20));

userList.stream()

.filter(user -> user.getAge() > 18)

.map(User::getName)

.forEach(System.out::println);

}

}

点进filter方法,你会发现它的参数就是一个函数式接口Predicate:

我们可以从中得到启发:函数式接口不同于以往的普通接口,它最大的作用其实是为了支持行为参数传递,比如传递Lambda、方法引用、函数式接口对应的实例对象等。

函数式接口的玩法介绍

public class FunctionalInterfaceTest {

// 1.写了一个方法,参数是函数式接口,你可以传递Runnable的实现,也可以使用Lambda或方法引用

public static void execute(Runnable runnable) {

try {

runnable.run();

} catch (Exception e) {

e.printStackTrace();

}

}

public static void main(String[] args) {

// 2.传入匿名对象

execute(new Runnable() {

@Override

public void run() {

System.out.println("匿名对象");

}

});

// 3.使用Lambda,()表示Runnable#run()的参数,println()是Runnable#run()的方法体

execute(() -> System.out.println("使用lambda"));

// 5.因为wrapPrintln和上面的println做的是同样的事,可以替换

UserService userService = new UserService();

execute(() -> userService.wrapPrintln());

// 6.IDEA提示上面的代码可以优化成 方法引用

execute(userService::wrapPrintln);

// 8.你会发现上面的写法仍是对的,因为“仅有一个抽象方法”是对Runnable的约束,不要搞混

}

// 4.我们试着把println()移到wrapPrintln中

static class UserService {

public void wrapPrintln() {

System.out.println("包装后的println");

}

// 7.给UserService新增一个方法

public void anotherMethod() {

System.out.println("另一个方法,不影响execute使用wrapPrintln");

}

}

}

使用场景1

经过上面的铺垫,你应该能理解下面的写法,这是我在某课时间看到的一段代码。

public void sendBook() {

try {

this.service.sendBook();

} catch (Throwable t) {

this.notification.send(new SendFailure(t)));

throw t;

}

}

public void sendChapter() {

try {

this.service.sendChapter();

} catch (Throwable t) {

this.notification.send(new SendFailure(t)));

throw t;

}

}

public void startTranslation() {

try {

this.service.startTranslation();

} catch (Throwable t) {

this.notification.send(new SendFailure(t)));

throw t;

}

}

代码本身没有问题,但可以优化。上面三个方法都有相同的异常处理方式,可以抽取成通用模板:

private void execute(final Runnable runnable) {

try {

runnable.run();

} catch (Throwable t) {

this.notification.send(new SendFailure(t)));

throw t;

}

}

然后把原来的方法作为参数传进去:

public void sendBook() {

execute(this.service::sendBook);

}

public void sendChapter() {

execute(this.service::sendChapter);

}

public void startTranslation() {

execute(this.service::startTranslation);

}

使用场景2

再介绍一个前几天写代码时遇到的一个场景:

我自定义了一个权限注解@RequiresPermission,如果被加在Controller上,那么内部所有接口都要权限校验,否则只校验加了@RequiresPermission的接口。但无论如何,我要先收集需要权限校验的接口。

@Component

public class PermissionMethodCollectionListener

implements ApplicationListener,

ApplicationContextAware {

// 省略其他方法

@Override

public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {

// ...

for (Object bean : beans) {

Class> controllerClazz = bean.getClass();

Method[] methods = controllerClazz.getMethods();

/*

* 1.判断并收集接口:

* 1.1 如果Controller上有@RequiresPermission,收集所有的接口

* 1.2 如果Controller上没有@RequiresPermission,那么只收集有@RequiresPermission的接口

* */

// 2.存储需要权限认证的接口

}

// ...

}

}

最直观的代码是:

@Component

public class PermissionMethodCollectionListener

implements ApplicationListener,

ApplicationContextAware {

// ...

@Override

public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {

// ...

for (Object bean : beans) {

Class> controllerClazz = bean.getClass();

Method[] methods = controllerClazz.getMethods();

Annotation annotationOnController = AnnotationUtils.findAnnotation(controllerClazz, RequiresPermission.class);

if (annotationOnController == null) {

// Controller上没注解,那么收集 @RequiresPermission + @RequestMapping 的方法

Arrays.stream(methods)

.filter(this::hasPermissionAnnotation)

.map(method -> {

StringBuilder sb = new StringBuilder();

String methodName = method.getName();

return sb.append(controllerClazz.getSimpleName()).append("#").append(methodName).toString();

})

.collect(Collectors.toSet());

} else {

// Controller有注解,那么收集 @RequestMapping 的方法

Arrays.stream(methods)

.filter(this::isApiMethod)

.map(method -> {

StringBuilder sb = new StringBuilder();

String methodName = method.getName();

return sb.append(controllerClazz.getSimpleName()).append("#").append(methodName).toString();

})

.collect(Collectors.toSet());

}

// ...

}

}

private boolean hasPermissionAnnotation(Method method) {

// 打了@RequiresPermission + @RequestMapping

return AnnotationUtils.findAnnotation(method, RequestMapping.class) != null

&& AnnotationUtils.findAnnotation(method, RequiresPermission.class) != null;

}

private boolean isApiMethod(Method method) {

// 打了@RequestMapping注解

return AnnotationUtils.findAnnotation(method, RequestMapping.class) != null;

}

}

你会发现,有两坨Stream代码,他们的差别就是filter,所以可以把Stream的代码抽出来,把filter作为参数传递:

@Component

public class PermissionMethodCollectionListener

implements ApplicationListener,

ApplicationContextAware {

// ...

@Override

public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {

// ...

for (Object bean : beans) {

Class> controllerClazz = bean.getClass();

// 如果Controller上有@RequiresPermission,那么所有接口都要收集(isApiMethod),否则只收集打了@Permission的接口(hasPermissionAnnotation)

Predicate filter = AnnotationUtils.findAnnotation(controllerClazz, RequiresPermission.class) != null

? this::isApiMethod

: this::hasPermissionAnnotation;

// 过滤出Controller中需要权限验证的method

Set permissionMethodsWithinController = getPermissionMethodsWithinController(

controllerClazz.getName(),

controllerClazz.getMethods(),

filter

);

// ...

}

}

private Set getPermissionMethodsWithinController(String controllerName, Method[] methods, Predicate filter) {

return Arrays.stream(methods)

.filter(filter)

.map(method -> {

StringBuilder sb = new StringBuilder();

String methodName = method.getName();

return sb.append(controllerName).append("#").append(methodName).toString();

})

.collect(Collectors.toSet());

}

private boolean hasPermissionAnnotation(Method method) {

return AnnotationUtils.findAnnotation(method, RequestMapping.class) != null

&& AnnotationUtils.findAnnotation(method, RequiresPermission.class) != null;

}

private boolean isApiMethod(Method method) {

return AnnotationUtils.findAnnotation(method, RequestMapping.class) != null;

}

}

博主自己写的Java小册已经开始出售,欢迎加入一起学习。

小册介绍:https://zhuanlan.zhihu.com/p/212191791​zhuanlan.zhihu.comzhihu-card-default.svg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值