前言
关于为啥写这一篇博客呢!三个答案…
第一个是我上个星期参加了公司的代码评审会议,在会议上就我写的服务展开了分析,结论是别的都还好,就是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 case
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() 方法进行过滤
1、创建两个方法名不同的方法
public String student(String param){
return "调用学生:"+param+"方法成功";
}
public String teacher(String param){
return "调用老师:"+param+"方法成功";
}
2、调用
public String test(String param) throws Exception {
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
1、先创建一个接口
public interface UserService {
String getTask();
}
2、两个实现类分别实现上面那个接口
@Service("student")
public class StudentServiceImpl implements UserService {
@Override
public String getTask() {
return "学生在学习";
}
}
@Service("teacher")
public class TeacherServiceImpl implements UserService {
@Override
public String getTask() {
return "老师在教书";
}
}
3.1、直接使用Map注入
在服务启动后,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);
}
}
3.2、也可使用getBean进行调用
从应用的上下文中获取指定bean对象,相信各位同学对spring容器都熟悉
@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 ,同时也伴随着一种函数式编程的说话,这里主要举例说明两种函数
- Function<T,R>:有输入,有输出,T代表输入,R代表输出
- Supplier:只要输出没有输入
- 想了解其他函数可以查看我上一篇博客:Lamdba表达式详解(一篇解决lamdba表达式)
1、优化前:
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 "找不到对应的方法";
}
}
2、优化后:
当方法都有输入和输出时,我们就使用Function<T,R> 函数,调用apply() 方法执行
public String student(String param){
return "调用学生:"+param+"方法成功";
}
public String teacher(String param){
return "调用老师:"+param+"方法成功";
}
public String test(String param){
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);
}
七、使用Java8的新特性Optional判断是否为null
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中的值存在,则执行该方法的调用,否则什么也不做 |
1、优化前:
String test6(String param){
if (param != null){
return param;
}else {
return "错误";
}
}
2、优化后:
String test6(String param){
return Optional.ofNullable(param).orElse("错误");
}
查看底层我们可以发现,其实就是一个三目运算
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
3、也可以使用Optional进行一个责任链
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;
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.FALSE));
}
private Optional<Leader> getLeaderByEmployeeId(Long employeeId) {
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 代码优化 提高代码质量–进阶之路
任何执拗都会成为过往,只有时间会告诉你对错。
|