关于为啥写这一篇博客呢!三个答案…
第一个是我上个星期参加了公司的代码评审会议,在会议上就我写的服务展开了分析,结论是别的都还好,就是
if...else
太多了,影响阅读,而且不美观,对的,就是不美观
,在公司来说,这也是很重要的一点,代码整洁度直接影响到阅读体验,而且我自己在给别人解释时,也会有因为这种情况而短路的情况;
第二个就是我自己也挺恶心的,还有就是我朋友写了一篇这样的文章,看了一下,收益匪浅,但总感觉差了点东西
第三个也是最重要的一点,这个月还没写过一篇呢,哈哈,被我朋友催着学习了
那么…就开始吧!!
三目运算符是基础不做过多解释,直接看代码
优化前:
Integer test6(String param){
if (param.equals("string")){
return 1;
}else {
return 0;
}
}
优化后:
Integer test6(String param){
return param.equals("string") ?1:0;
}
switch也是基础语句,不做过多解释,直接看代码
优化前:
private String getCn(String en) {
String cn;
if ("student".equals(en)) {
cn = "学生";
} else if ("teacher".equals(en)) {
cn = "教师";
} else {
cn = "未知";
}
return cn;
}
优化后:
private String getCn(String en) {
String cn;
switch(en) {
case "student":
return "学生";
case "teacher":
return "教师";
default:
return "未知";
}
}
如果传进来的参数是在指定范围的,那最好定义在枚举中
public enum CnEnum {
STUDENT("student", "学生"),
TEACHER("teacher", "教师"),
UNKNOWN("unKnown", "未知");
private String en;
private String cn;
public String getEn() {
return en;
}
public String getCn() {
return cn;
}
CnEnum(String en, String cn) {
this.en = en;
this.cn = cn;
}
//外界根据条件调用该枚举类的,获取枚举值的核心方法
static String of(String en) {
for (CnEnum temp : CnEnum.values()) {
if (temp.getEn().equals(en)) {
return temp.getCn();
}
}
return CnEnum.valueOf("UNKNOWN").getCn();
}
}
也可以调用isValidEnumIgnoreCase()
方法判断指定参数是否是有效枚举,可以简单理解成枚举类中是否包含该元素
//判断来源是否符合规范
if (EnumUtils.isValidEnumIgnoreCase(CnEnum.class,参数)) {
//包含
return true;
}
场景: 根据指定参数调用对应的方法
思路: 其实这个方法在以前刚学习反射时就已经用过,就是创建当前对象的反射对,调用getDeclaredMethod()
方法获取方法对象,然后调用invoke()
执行就可
可以把方法名添加到枚举中,参数进来时先调用枚举的
isValidEnumIgnoreCase()
方法进行过滤
public String student(String param){
return "调用学生:"+param+"方法成功";
}
public String teacher(String param){
return "调用老师:"+param+"方法成功";
}
public String test(String param) throws Exception {
//可以把方法名添加到枚举中,参数进来时先调用枚举的`isValidEnumIgnoreCase()`方法进行过滤
//获取当前对象的反射对象
Class<? extends DemoServiceImpl> aClass = this.getClass();
//获取方法对象
Method method = aClass.getDeclaredMethod(param, String.class);
//执行方法
String invoke = (String) method.invoke(this, param);
return invoke;
}
表驱动法是指我们可以将信息、方法、以及对象存在表中,这个表可以是list,可以是map,也可以是数组,更可以是spring容器
…,从而我们可以直接从表中取,而不需要if…else
public interface UserService {
//获取用户的主要任务
String getTask();
}
@Service("student")
public class StudentServiceImpl implements UserService {
@Override
public String getTask() {
return "学生在学习";
}
}
@Service("teacher")
public class TeacherServiceImpl implements UserService {
@Override
public String getTask() {
return "老师在教书";
}
}
在服务启动后,userMap就会存在两个元素,(“student”,StudentServiceImpl)
与(“teacher”,TeacherServiceImpl)
Spring会自动地将形如(@Service后面的名称,实现该接口的类)注入到该userMap中
@Service
public class UserContext {
@Autowired
Map<String, UserService> userMap;
public UserService getUserService(String type) {
return userMap.get(type);
}
}
从应用的上下文中获取指定bean对象,相信各位同学对spring容器都熟悉
/**
* 获取不同bean对象
*/
@Resource
private ApplicationContext applicationContext;
public UserService create(String type) {
return applicationContext.getBean(type, UserService.class);
}
@Service
public class UserContext {
public String getUserService(String type) {
UserService userService = create(type);
String task = userService.getTask();
return task;
}
}
表驱动法在第五点里已经解释过了
java8很重要的新特性 Lamdba
,同时也伴随着一种函数式编程的说话,这里主要举例说明两种函数
public String student(String param){
return "调用学生:"+param+"方法成功";
}
public String teacher(String param){
return "调用老师:"+param+"方法成功";
}
//调用
public String test(String param){
if (param.equals("student")){
return student(param);
}else if (param.equals("teacher")){
return teacher(param);
}else {
return "找不到对应的方法";
}
}
当方法都有输入和输出时,我们就使用Function
函数,调用apply()
方法执行
public String student(String param){
return "调用学生:"+param+"方法成功";
}
public String teacher(String param){
return "调用老师:"+param+"方法成功";
}
//调用,如果没有输入,就是用Supplier函数,一样的用法
public String test(String param){
//创建一个map,value为function函数,第一个string为入参,第二个string为返回对象
HashMap<String, Function<String, String>> map = new HashMap<>();
map.put("student",this::student);
map.put("teacher",this::teacher);
return map.get(param).apply(param);
}
还可以使用代码块启动就加入map中,如果是多次使用的话建议这样,但只是使用一次的话还是建议直接写在方法里;
private static HashMap<String, Function<String, String>> map = new HashMap<>();
{
map.put("student",this::student);
map.put("teacher",this::teacher);
}
//调用
public String test(String param){
return map.get(param).apply(param);
}
Optional是Java8的新特性
,他可以对元素进行非空判断,然后执行对应的方法或者只做判断,也可以结合函数进行操作,但不能很好的结合stream
,下面是Optional类库的一些方法说明:
方法 | 描述 |
---|---|
of | 把指定的值封装为Optional对象,如果指定的值为null,则抛出NullPointerException |
empty | 创建一个空的Optional对象 |
ofNullable | 把指定的值封装为Optional对象,如果指定的值为null,则创建一个空的Optional对象 |
get | 如果创建的Optional中有值存在,则返回此值,否则抛出NoSuchElementException |
orElse | 如果创建的Optional中有值存在,则返回此值,否则返回一个默认值 |
orElseGet | 如果创建的Optional中有值存在,则返回此值,否则返回一个由Supplier接口生成的值 |
orElseThrow | 如果创建的Optional中有值存在,则返回此值,否则抛出一个由指定的Supplier接口生成的异常 |
filter | 如果创建的Optional中的值满足filter中的条件,则返回包含该值的Optional对象,否则返回一个空的Optional对象 |
map | 如果创建的Optional中的值存在,对该值执行提供的Function函数调用 |
flagMap | 如果创建的Optional中的值存在,就对该值执行提供的Function函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象 |
isPresent | 如果创建的Optional中的值存在,返回true,否则返回false |
ifPresent | 如果创建的Optional中的值存在,则执行该方法的调用,否则什么也不做 |
String test6(String param){
if (param != null){
return param;
}else {
return "错误";
}
}
String test6(String param){
return Optional.ofNullable(param).orElse("错误");
}
查看底层我们可以发现,其实就是一个三目运算
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
Optional的真正作用并不是在 “告诉观众这个数据有可能为null” 上,而是提供了一个可以把多个结果有可能为null的操作串起来最终得到一个有可能为null结果的容器
。而这种写法的真正好处并不是“这堆东西其中一步可能是null”,而是明确告诉观众“这堆操作是一个逻辑整体而且它们形成了一个pipe(前者的输出是且只是后者的输入)”。
例如Optional最经典的null-safe get chain。
return Optional.ofNullable(a)
.map(A::getB)
.map(B::getC)
.map(C::getD)
.orElse(null);
很明显像上面这样写多多少少有点不严谨,我们举例说明:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.math.BigDecimal;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Leader {
private Long employeeId;
private BigDecimal bonus;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Employee {
private Long id;
private String name;
private Boolean leader;
}
import java.util.Optional;
/**
* 找到ID为1的员工,如果有奖金就打印出来,没有就打印没有奖金;
* @throws
*/
public class FiltUtil {
public void OptionalStudy() {
Optional<Leader> leader = Optional.ofNullable(getEmployeeById(1L)
.filter(Employee::getLeader)
.map(Employee::getId)
.flatMap(this::getLeaderByEmployeeId)
.orElse(null));
if (leader.isPresent()) {
Optional.of(leader.map(Leader::getBonus).map(bonus -> String.format("员工ID为1的leader奖金为:%s", bonus)).orElse("员工ID为1的leader也没有奖金")).ifPresent(System.out::println);
} else {
System.out.println("员工ID为1的leader未找到,他可能只是一个基层员工,不配拥有奖金");
}
}
private Optional<Employee> getEmployeeById(Long id) {
//return Optional.of(new Employee(1L, "大老板", Boolean.TRUE));
return Optional.of(new Employee(1L, "大老板", Boolean.FALSE));
}
private Optional<Leader> getLeaderByEmployeeId(Long employeeId) {
//return employeeId == 1L ? Optional.of(new Leader(1L, BigDecimal.valueOf(1000000000))) : Optional.empty();
return employeeId == 1L ? Optional.of(new Leader(1L, null)) : Optional.empty();
}
//主函数测试
public static void main(String[] args) {
FiltUtil filtUtil = new FiltUtil();
filtUtil.OptionalStudy();
}
}
其实本人不是很喜欢用Optional,因为它虽然使你的代码优雅了,但失去了逻辑性,对后期的维护,以及交接都很不利,而且如果你只是用来非空判断的话,很明显有别的更好的选择,例如:apache的工具类,hutool的工具类,都比Optional更直接
所以对于Optional得态度就是喜欢就用,不喜欢就不用
所有方法没有说哪个比哪个好,都是在不同的场景,就会有不同的选择,希望同学们理性使用,有任何问题都可以评论或私聊,最后送上《阿里云的这群疯子》中的一句话:
参考资料:if…else 代码优化 提高代码质量–进阶之路
任何执拗都会成为过往,只有时间会告诉你对错。