博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JDK8漫谈——代码更优雅
阅读量:7172 次
发布时间:2019-06-29

本文共 8145 字,大约阅读时间需要 27 分钟。

简介

lambda表达式,又称闭包(Closure)或称匿名方法(anonymous method)。将Lambda表达式引入JAVA中的动机源于一个叫“行为参数”的模式。这种模式能够解决需求变化带来的问题,使代码变得更加灵活。在JAVA8之前,参数模式十分啰嗦。Lambda表达式通过精简的方式使用行为模式克服了这个缺点

解决什么问题

  • 传递行为。它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理,变成了一等公民。解决重复的代码片段和代码包裹问题。
  • 内置抽象行为。把常见的行为定义成接口,可以直接使用,减少重复思考和代码。都在java.util.function包里
  • 更少的代码。通过类型推断,方法引用,可以让代码更优雅。

背后思想

函数式编程

内容说明

1. 作用域

  • this
    在内部类中,this指向当前内部类对象自己,而在lambda表达式中,this指向的是表达式外部的类对象。
public class ScopeTest {    @Test    public void test_scope(){        Runnable runnable = () -> {            this.print();        };        runnable.run();    }    private void print(){        System.out.println("I can print");    }}复制代码
  • final

    labmda表达式使用外部的变量时,不可修改,默认定义成final

  • 函数式接口

    只有一个抽象方法的接口我们就称之为功能性接口,又简称 SAM 类型,即 Simple Abstract Method。会写上注释 @FunctionalInterface

//用这个注解来表示功能性接口public interface Consumer
{ /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t);}复制代码

常见的内置函数如下:

  • java.lang.Runnable 执行动作

  • java.util.function.Predicate 接收T对象并返回boolean

  • java.util.function.Consumer 接收T对象,不返回值

  • java.util.function.Function<T,R> 接收T对象,返回R对象.

  • java.util.function.Supplier 提供T对象(例如工厂),不接收值

  • 方法引用

    方法引用有很多种,它们的语法如下:

  • 静态方法引用:ClassName::methodName

  • 实例上的实例方法引用:instanceReference::methodName

  • 超类上的实例方法引用:super::methodName

  • 类型上的实例方法引用:ClassName::methodName

  • 构造方法引用:Class::new

  • 数组构造方法引用:TypeName[]::new

@Test    public void test_instance(){        Set
girls = new HashSet<>(); Set
names = new HashSet<>(); names.stream() //实例::methodName .filter(girls::contains) .collect(Collectors.toList()); } @Test public void test_this(){ Set
names = new HashSet<>(); names.stream() //this::methodName .filter(this::hasAuth) .collect(Collectors.toList()); } private boolean hasAuth(String authKey){ return true; }复制代码
  • 类型推断
Map
map = new HashMap<>(); map.forEach((String name,Person person)->{ person.fly(); System.out.println(name+":"+person); }); map.forEach((name,person)->{ // 无须判断类型,自动根据上下文推断 person.fly(); System.out.println(name+":"+person); });复制代码

2. 实践

  • 最佳实践
    消灭代码片段
/**     * 正常的代码     */    @Test    public void test_person(){        CnResult
result = null; try { // 只有这里取值是不同的,其他的处理是一样的,包括try,catch,打日志,定义异常等 Person entity = this.getPerson(); result = CnResult.success(entity); }catch (CnException e){ logger.error(e.getMessage(),e); result = CnResult.error(e.getErrorCode(),e.getMessage()); } catch (Exception e) { logger.error(e.getMessage(),e); result = CnResult.error("1-1-1-1",e.getMessage()); } Assert.assertNotNull(result); } @Test public void test_animal(){ CnResult
result = null; try { // 只有这里取值是不同的,其他的处理是一样的,包括try,catch,打日志,定义异常等 Animal entity = this.getAnimal(); result = CnResult.success(entity); }catch (CnException e){ logger.error(e.getMessage(),e); result = CnResult.error(e.getErrorCode(),e.getMessage()); } catch (Exception e) { logger.error(e.getMessage(),e); result = CnResult.error("1-1-1-1",e.getMessage()); } Assert.assertNotNull(result); } /** * lambda代码 */ @Test public void test_lambda(){ //屏蔽所有细节,只把获取对象的逻辑传递进去 CnResult
person = WapperUtils.wapper(this::getPerson); Assert.assertNotNull(person); CnResult
animal = WapperUtils.wapper(this::getAnimal); Assert.assertNotNull(animal); } public class WapperUtils { private static final Logger logger = LoggerFactory.getLogger(WapperUtils.class); /** * 包裹 * * @param supplier * @param
* @return */ public static
CnResult
wapper(Supplier
supplier){ try { T entity = supplier.get(); CnResult
cnResult = CnResult.success(entity); return cnResult; }catch (CnException e){ logger.error(e.getMessage(),e); return CnResult.error(e.getErrorCode(),e.getMessage()); } catch (Exception e) { logger.error(e.getMessage(),e); return CnResult.error("1-1-1-1",e.getMessage()); } } }复制代码

只要是代码片段,找到变点化,抽象成lambda,把固定的代码统一封装,就可以使用行为的复用,减少代码片段和代码包裹。

明确语义

@Test    public void test_first() {        List
persons = new ArrayList<>(); persons.stream() /** * 没有明确的语义。需要根据过程计算推理得出结果,也不清楚背后的业务和场景 * */ .filter(person -> person.getAge() >= 18) .collect(Collectors.toList()); } @Test public void test_second() { List
persons = new ArrayList<>(); persons.stream() .filter(person -> { /** * 如果职责变更,得修改代码结构,而且代码越来越难理解 */ if (person.getAge() >= 18) { return true; } if (person.getName().startsWith("庄")) { return true; } return false; }) .collect(Collectors.toList()); } @Test public void test_third() { List
persons = new ArrayList<>(); persons.stream() /** * 随着业务变更需要不断添加filter */ .filter(person -> person.getAge() >= 18) .filter(person -> person.getName().startsWith("庄")) .collect(Collectors.toList()); } @Test public void test_fourth() { List
persons = new ArrayList<>(); persons.stream() /** * 随着业务变更需要不断添加filter */ .filter(Person::isAdult) .filter(Person::belongToZhuang) .collect(Collectors.toList()); } @Test public void test_final() { List
persons = new ArrayList<>(); /** * 有明确的语义,不用再面向细节加工一下,而且也方便后面的扩展 */ persons.stream() .filter(Person::hasAuth) .collect(Collectors.toList()); }复制代码
  • 最佳反例
    随意取名
@Test    public void test_name_bad(){        List
persons = new ArrayList<>(); /** * lambda一多的时候,没有明确的参数,计算和理解起来非常吃力。 */ persons.stream() .filter(e->e.getAge()>18) .map(e->e.getName()) .filter(e->e.startsWith("庄")) .collect(Collectors.toList()); } @Test public void test_name(){ List
persons = new ArrayList<>(); persons.stream() .filter(person->person.getAge()>18) .map(person->person.getName()) .filter(name->name.startsWith("庄")) .collect(Collectors.toList()); }复制代码

参数需要有明确语义

逻辑复杂

@Test    public void test_bad() {        List
values = new ArrayList<>(); int result = values.stream().mapToInt(e -> { int sum = 0; for (int i = 0; i < e; i++) { if (e % i == 0) { sum += i; } } return sum; }).sum(); System.out.println(result); } @Test public void test_() { List
values = new ArrayList<>(); int result = values.stream().mapToInt(this::toInt).sum(); System.out.println(result); } private Integer toInt(int e) { int sum = 0; for (int i = 0; i < e; i++) { if (e % i == 0) { sum += i; } } return sum; }复制代码
  • 难以读懂
  • 用途不明
  • 难以测试
  • 难以复用

建议把多行lambda表达式主体转移到一个命名函数中,然后使用方法引用

  • 思考
    • 和内部类的区别
    • 和AOP的区别

好消息

腾讯云现在做活动,新手福利1000减750,云服务器最低2折,1核2G内存50G硬盘3年最低只要600元!


转自:https://www.cnblogs.com/ansn001/p/9399360.html

转载于:https://juejin.im/post/5b612361f265da0f473536a1

你可能感兴趣的文章
leetcode974
查看>>
leetcode1099
查看>>
JAVA RPC(二)序列化协议杂谈
查看>>
jQuery中的文档处理
查看>>
JProfiler 8(一个很好的java性能监控工具) 下载和注册码
查看>>
微信小程序wx.chooseImage和wx.previewImage的综合使用(图片上传不限制最多张数)...
查看>>
SQL SERVER数据库日常使用总结
查看>>
搭建Nginx服务
查看>>
细解字符串长度
查看>>
html lable filedset
查看>>
Python 函数式编程
查看>>
Good moring!
查看>>
关于“问吧”调查问卷的心得体会
查看>>
UVA-307 Sticks
查看>>
Bad days
查看>>
PHP基础笔记
查看>>
Android 音视频深入 十八 FFmpeg播放视频,有声音(附源码下载)
查看>>
扩展KMP模板
查看>>
php 分页原理+分页代码+分页类制作
查看>>
CSS选择器要点笔记
查看>>