Lambda表达式最佳实践
简介
Lambda表达式java 8引入的函数式编程框架。之前的文章中我们也讲过Lambda表达式的基本用法。
本文将会在之前的文章基础上更加详细的讲解Lambda表达式在实际应用中的最佳实践经验。
优先使用标准Functional接口
之前的文章我们讲到了,java在java.util.function包中定义了很多Function接口。基本上涵盖了我们能够想到的各种类型。
假如我们自定义了下面的Functional interface:
@FunctionalInterface
public interface Usage {
String method(String string);
}
然后我们需要在一个test方法中传入该interface:
public String test(String string, Usage usage) {
return usage.method(string);
}
上面我们定义的函数接口需要实现method方法,接收一个String,返回一个String。这样我们完全可以使用Function来代替:
public String test(String string, Function<String, String> fn) {
return fn.apply(string);
}
使用标准接口的好处就是,不要重复造轮子。
使用@FunctionalInterface注解
虽然@FunctionalInterface不是必须的,不使用@FunctionalInterface也可以定义一个Functional Interface。
但是使用@FunctionalInterface可以在违背Functional Interface定义的时候报警。
如果是在维护一个大型项目中,加上@FunctionalInterface注解可以清楚的让其他人了解这个类的作用。
从而使代码更加规范和更加可用。
所以我们需要这样定义:
@FunctionalInterface
public interface Usage {
String method(String string);
}
而不是:
public interface Usage {
String method(String string);
}
在Functional Interfaces中不要滥用Default Methods
Functional Interface是指只有一个未实现的抽象方法的接口。
如果该Interface中有多个方法,则可以使用default关键字为其提供一个默认的实现。
但是我们知道Interface是可以多继承的,一个class可以实现多个Interface。 如果多个Interface中定义了相同的default方法,则会报错。
通常来说default关键字一般用在升级项目中,避免代码报错。
使用Lambda 表达式来实例化Functional Interface
还是上面的例子:
@FunctionalInterface
public interface Usage {
String method(String string);
}
要实例化Usage,我们可以使用new关键词:
Usage usage = new Usage() {
@Override
public String method(String string) {
return string;
}
};
但是最好的办法就是用lambda表达式:
Usage usage = parameter -> parameter;
不要重写Functional Interface作为参数的方法
怎么理解呢? 我们看下面两个方法:
public class ProcessorImpl implements Processor {
@Override
public String process(Callable<String> c) throws Exception {
// implementation details
}
@Override
public String process(Supplier<String> s) {
// implementation details
}
}
两个方法的方法名是一样的,只有传入的参数不同。但是两个参数都是Functional Interface,都可以用同样的lambda表达式来表示。
在调用的时候:
String result = processor.process(() -> "test");
因为区别不了到底调用的哪个方法,则会报错。
最好的办法就是将两个方法的名字修改为不同的。
Lambda表达式和内部类是不同的
虽然我们之前讲到使用lambda表达式可以替换内部类。但是两者的作用域范围是不同的。
在内部类中,会创建一个新的作用域范围,在这个作用域范围之内,你可以定义新的变量,并且可以用this引用它。
但是在Lambda表达式中,并没有定义新的作用域范围,如果在Lambda表达式中使用this,则指向的是外部类。
我们举个例子:
private String value = "Outer scope value";
public String scopeExperiment() {
Usage usage = new Usage() {
String value = "Inner class value";
@Override
public String method(String string) {
return this.value;
}
};
String result = usage.method("");
Usage usageLambda = parameter -> {
String value = "Lambda value";
return this.value;
};
String resultLambda = usageLambda.method("");
return "Results: result = " + result +
", resultLambda = " + resultLambda;
}
上面的例子将会输出“Results: result = Inner class value, resultLambda = Outer scope value”
Lambda Expression尽可能简洁
通常来说一行代码即可。如果你有非常多的逻辑,可以将这些逻辑封装成一个方法,在lambda表达式中调用该方法即可。
因为lambda表达式说到底还是一个表达式,表达式当然越短越好。
java通过类型推断来判断传入的参数类型,所以我们在lambda表达式的参数中尽量不传参数类型,像下面这样:
(a, b) -> a.toLowerCase() + b.toLowerCase();
而不是:
(String a, String b) -> a.toLowerCase() + b.toLowerCase();
如果只有一个参数的时候,不需要带括号:
a -> a.toLowerCase();
而不是:
(a) -> a.toLowerCase();
返回值不需要带return:
a -> a.toLowerCase();
而不是:
a -> {return a.toLowerCase()};
使用方法引用
为了让lambda表达式更加简洁,在可以使用方法引用的时候,我们可以使用方法引用:
a -> a.toLowerCase();
可以被替换为:
String::toLowerCase;
Effectively Final 变量
如果在lambda表达式中引用了non-final变量,则会报错。
effectively final是什么意思呢?这个是一个近似final的意思。只要一个变量只被赋值一次,那么编译器将会把这个变量看作是effectively final的。
String localVariable = "Local";
Usage usage = parameter -> {
localVariable = parameter;
return localVariable;
};
上面的例子中localVariable被赋值了两次,从而不是一个Effectively Final 变量,会编译报错。
为什么要这样设置呢?因为lambda表达式通常会用在并行计算中,当有多个线程同时访问变量的时候Effectively Final 变量可以防止不可以预料的修改。
结论
lambda是一个非常有用的功能,希望小伙伴们能够在工作中掌握。
欢迎关注我的公众号:程序那些事,更多精彩等着您!
更多内容请访问 www.flydean.com
Lambda表达式最佳实践的更多相关文章
- 5万字长文:Stream和Lambda表达式最佳实践-附PDF下载
目录 1. Streams简介 1.1 创建Stream 1.2 Streams多线程 1.3 Stream的基本操作 Matching Filtering Mapping FlatMap Reduc ...
- Java 8里面lambda的最佳实践
Java 8已经推出一段时间了,越来越多开发人员选择升级JDK,这条热门动弹里面看出,JDK7最多,其次是6和8,这是好事! 在8 里面Lambda是最火的主题,不仅仅是因为语法的改变,更重要的是带来 ...
- Lambda表达式和函数试接口的最佳实践 · LiangYongrui's Studio
1.概述 本文主要深入研究java 8中的函数式接口和Lambda表达式,并介绍最佳实践. 2.使用标准的函数式接口 包java.util.function中的函数是接口已经可以满足大部分的java开 ...
- 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生
[转].NET(C#):浅谈程序集清单资源和RESX资源 目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...
- javascript之小积累-匿名函数表达式的最佳实践
在写js的时候,还是经常会用的匿名函数表达式,比如 setTimeout(function() { console.log(110); }, 1000); 上面那个function()就是匿名函数表达 ...
- Java8函数式接口以及lambda表达式实践
罗列一下遇到可以转换成lamada表达式的场景,仅供参考,如有更好的方式,欢迎在评论区留言. 1.计算订单总金额 订单总金额一般是在后台循环叠加每个购买商品的金额已获取到,通常的方式如下 BigDec ...
- Java8 Lambda表达式和流操作如何让你的代码变慢5倍
原文出处:ImportNew 有许许多多关于 Java 8 中流效率的讨论,但根据 Alex Zhitnitsky 的测试结果显示:坚持使用传统的 Java 编程风格——iterator 和 for- ...
- [转]Android开发最佳实践
——欢迎转载,请注明出处 http://blog.csdn.net/asce1885 ,未经本人同意请勿用于商业用途,谢谢—— 原文链接:https://github.com/futurice/and ...
- 10个精妙的Java编码最佳实践
这是一个比Josh Bloch的Effective Java规则更精妙的10条Java编码实践的列表.和Josh Bloch的列表容易学习并且关注日常情况相比,这个列表将包含涉及API/SPI设计中不 ...
随机推荐
- CCF题库刷题编译错误
最近在CCF上刷题,因为C语言更合适,就使用了devc编译器,选择C语言但是却报编译错误 后来查了一下,发现在提交时选择C++语言就能满分通过,问题得以解决.
- 关于$f(x)=\int_0^x\left|\sin\frac1t\right|\text dt$求导的问题
首先,我们考虑\(f(x)\)在\(\mathbb R\)上都是定义的.根据定义,显然有\(f(0)=0\):其次,对于\(x\neq0\),不妨先设\(x\gt0\),则有在\(t\rightarr ...
- git基础使用合集
1.git初始化仓库-git init git init 创建一个.git目录,跟踪管理版本 2.git 添加-git add git add xxx.xxx 添加到暂缓区里 git add * 添加 ...
- 测试老司机都在用的表白神器你会吗?-Fiddler之AutoResponse线上调试
一.Fiddler在线调试功能和表白神器介绍 在以往的工作中,线上有bug,就需要把文件弄到本地来改,但经常会碰见本地环境又和线上不一样,导致调试困难,闭着眼睛改好之后传到线上去看对不对,不对的话 ...
- python 网络编程---粘包
一.什么是粘包?(只有在TCP中有粘包现象,在UDP中永远不会粘包) 黏包不一定会发生. 如果发生 了:1.可能是在客户端已经粘了 2.客户端没有粘,可能是在服务端粘了. 所谓的粘包问题:主要是是因为 ...
- String 对象-->toUpperCase() 方法
1.定义和用法 将字符串中所有的小写字符转换成大写字符,大写字符保持不变 返回转换后的结果字符串 语法: string.toUpperCase() 注意:不会改变字符串本身,仅以返回值的形式返回结果 ...
- python3(二)
# 布尔值和Java一样不做验证了 # 空值None是一个特殊的空值 # 变量 变量名必须是大小写英文.数字和_的组合,且不能用数字开头,等号=是赋值语句,可以把任意数据类型赋值给变量,同一个变量可以 ...
- MySQL锁---InnoDB行锁需要注意的细节
前言 换了工作之后,接近半年没有发博客了(一直加班),emmmm.....今天好不容易有时间,记录下工作中遇到的一些问题,接下来应该重拾知识点了.因为新公司工作中MySQL库经常出现查询慢,锁等待,节 ...
- hibernate.current_session_context_class 比较权威的解释
hibernate.current_session_context_class 博客分类: hibernate HibernateSpring多线程配置管理thread 遇到过的问题: 情景1: 在 ...
- 数据结构和算法(Golang实现)(13)常见数据结构-可变长数组
可变长数组 因为数组大小是固定的,当数据元素特别多时,固定的数组无法储存这么多的值,所以可变长数组出现了,这也是一种数据结构.在Golang语言中,可变长数组被内置在语言里面:切片slice. sli ...