优雅解析民族代码java_JDK8漫谈——代码更优雅

简介

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

解决什么问题

传递行为。它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理,变成了一等公民。解决重复的代码片段和代码包裹问题。

内置抽象行为。把常见的行为定义成接口,可以直接使用,减少重复思考和代码。都在java.util.function包里

更少的代码。通过类型推断,方法引用,可以让代码更优雅。

背后思想

函数式编程

内容说明

作用域

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

1c0099ae0614d65c49da1d79feedf018.png

7ca742bde7886e398a2fc9eed29213e0.png

函数式接口

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

@FunctionalInterface

//用这个注解来表示功能性接口

public interface Consumer {

/**

* Performs this operation on the given argument.

*

* @param t the input argument

*/

void accept(T t);

}

常见的内置函数如下:

name

function

java.lang.Runnable

执行动作

java.util.function.Predicate

接收T对象并返回boolean

java.util.function.Consumer

接收T对象,不返回值

java.util.function.Function

接收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);

});

实践

最佳实践

消灭代码片段

/**

* 正常的代码

*/

@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的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值