java 8 Optional 全面总结

目录

0. 导入

1. 使用

创建

map/flatMap/filter

2. 实战

不推荐当做参数使用

推荐作为返回值使用

异常对比

综合案例

项目中


0. 导入

首先,阅读这段代码,存在什么问题?

public class Person {
    private Car car;

    public Car getCar() {
        return this.car;
    }

    /**
     * 很容易的就空指针异常了啊
     *
     * @param person
     * @return
     */
    public String getCarInsuranceName(Person person) {
        return person.getCar().getInsurance().getName();
    }

    /**
     * 全是嵌套,if判断,no good
     *
     * @param person
     * @return
     */
    public String getCarInsuranceName2(Person person) {
        if (person != null) {
            Car car = person.getCar();
            if (car != null) {
                Insurance insurance = car.getInsurance();
                if (insurance != null) {
                    return insurance.getName();
                }
            }
        }
        return "unKnown";
    }

    /**
     * 这个多return,我如果漏了一个,顺序错了呢?
     *
     * @param person
     * @return
     */
    public String getCarInsuranceName3(Person person) {
        if (person == null) {
            return "unknown";
        }
        Car car = person.getCar();
        if (car == null) {
            return "unknown";
        }
        Insurance insurance = car.getInsurance();
        if (insurance == null) {
            return "unknown";
        }
        return insurance.getName();
    }
}

class Car {
    private Insurance insurance;
    public Insurance getInsurance() {
        return this.insurance;
    }
}

class Insurance {
    private String name;
    public String getName() {
        return this.name;
    }
}

这段代码是我们常见的对可能为null的对象的处理,对空指针的处理没有一个好的解决方案,基本都是增加判空。

  • Java 8推出java.util.Optional,对存在或者缺失的变量进行建模;

  • 使用Optional会迫使你更积极地解引用Optional对象,以应对变量值缺失的问题,最终,你能更有效地防止代码中出现不期而至的空指针异常。

  • 使用Optional能帮助你设计更好的API,用户只需要阅读方法签名,就能了解该方法是否接受一个Optional类型的值。

对刚刚的案例进行重构

public class Person {

    /**
     * 这个人有可能有,也可能没有,就需要包起来;
     */
    private Optional<Car> car;

    public Optional<Car> getCar() {
        return car;
    }
}
public class Car {

    /**
     * 同样的,car可能有保险,也可能没有
     */
    private Optional<Insurance> insurance;

    public Optional<Insurance> getInsurance() {
        return insurance;
    }
}
public class Insurance {

    /**
     * 保险公司必须有名字
     *
     * 总之:对模型进行了语义化;
     */
    private String name;

    public String getName() {
        return name;
    }
}
// 用法
public class OptionalMain {

    public String getCarInsuranceName(Optional<Person> person) {
        return person.flatMap(Person::getCar)
                .flatMap(Car::getInsurance)
                .map(Insurance::getName)
                .orElse("Unknown");
    }

    public Set<String> getCarInsuranceNames(List<Person> persons) {
        return persons.stream()
                .map(Person::getCar)
                .map(optCar -> optCar.flatMap(Car::getInsurance))
                .map(optInsurance -> optInsurance.map(Insurance::getName))
                .flatMap(Optional::stream)
                .collect(toSet());
    }
}

1. 使用

创建

Optional<Car> optCar = Optional.empty();

// 如果car是null,则会立刻抛出空指针异常
Optional<Car> optCar = Optional.of(car);

// car为空就返回Optional.empty();
Optional<Car> optCar = Optional.ofNullable(car);

map/flatMap/filter

// map方法
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);

// 下面这段代码有问题,参考上面这个图,结果是Optional<Optional<Car>>
Optional<Person> optPerson = Optional.of(person);
Optional<String> name =
optPerson.map(Person::getCar)
.map(Car::getInsurance)
.map(Insurance::getName);

// flatmap
public String getCarInsuranceName(Optional<Person> person) {
return person.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("Unknown");

其它API

public Insurance findCheapestInsurance(Person person, Car car) {
    // 找到最便宜的保险公司
    return new Insurance();
}

public Optional<Insurance> nullSafeFindCheapestInsurance(Optional<Person> person, Optional<Car> car) {
    if (person.isPresent() && car.isPresent()) {
        return Optional.of(findCheapestInsurance(person.get(), car.get()));
    } else {
        return Optional.empty();
    }
}

public Optional<Insurance> nullSafeFindCheapestInsurance2(Optional<Person> person, Optional<Car> car) {
    return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c)));
}

// filter
public String getCarInsuranceName(Optional<Person> person, int minAge) {
    return person.filter(p -> p.getAge() >= minAge)
                .flatMap(Person::getCar)
                .flatMap(Car::getInsurance)
                .map(Insurance::getName)
                .orElse("Unknown");
}

// orElse
asset.setFileName(Optional.ofNullable(assetObjectConfigMap.get("name")).orElse("No File Name!").toString());

2. 实战

不推荐当做参数使用

Java static code analysis: "Optional" should not be used for parameters

The Java language authors have been quite frank that Optional was intended for use only as a return type, as a way to convey that a method may or may not return a value.

And for that, it’s valuable but using Optional on the input side increases the work you have to do in the method without really increasing the value. With an Optional parameter, you go from having 2 possible inputs: null and not-null, to three: null, non-null-without-value, and non-null-with-value. Add to that the fact that overloading has long been available to convey that some parameters are optional, and there’s really no reason to have Optional parameters.

The rule also checks for Guava’s Optional, as it was the inspiration for the JDK Optional. Although it is different in some aspects (serialization, being recommended for use as collection elements), using it as a parameter type causes exactly the same problems as for JDK Optional.

public String sayHello(Optional<String> name) {  // Noncompliant
  if (name == null || !name.isPresent()) {
    return "Hello World";
  } else {
    return "Hello " + name;
  }
}

public String sayHello(String name) {
  if (name == null) {
    return "Hello World";
  } else {
    return "Hello " + name;
  }
}

推荐作为返回值使用

作为返回值使用有这样的好处,调用方知道返回的结果可能为空,就可以对数据进行有意识的判空处理;

public class Person {
    private Car car;
    public Optional<Car> getCarAsOptional() {
        return Optional.ofNullable(car);
    }
}

异常对比

public static Optional<Integer> stringToInt(String s) {
    try {
        return Optional.of(Integer.parseInt(s));
    } catch (NumberFormatException e) {
        return Optional.empty();
    }
}

综合案例

// 需求:读取属性为a的值,这个值大于0
@Test
public void testMap() {
    Properties props = new Properties();
    props.setProperty("a", "5");
    props.setProperty("b", "true");
    props.setProperty("c", "-3");

    assertEquals(5, readDurationImperative(props, "a"));
    assertEquals(0, readDurationImperative(props, "b"));
    assertEquals(0, readDurationImperative(props, "c"));
    assertEquals(0, readDurationImperative(props, "d"));

    assertEquals(5, readDurationWithOptional(props, "a"));
    assertEquals(0, readDurationWithOptional(props, "b"));
    assertEquals(0, readDurationWithOptional(props, "c"));
    assertEquals(0, readDurationWithOptional(props, "d"));
}

// 普通的方式
public static int readDurationImperative(Properties props, String name) {
    String value = props.getProperty(name);
    if (value != null) {
        try {
            int i = Integer.parseInt(value);
            if (i > 0) {
                return i;
            }
        } catch (NumberFormatException nfe) {
        }
    }
    return 0;
}

// java8的方式
public static int readDurationWithOptional(Properties props, String name) {
    return ofNullable(props.getProperty(name))
            .flatMap(ReadPositiveIntParam::s2i)
            .filter(i -> i > 0).orElse(0);
}

public static Optional<Integer> s2i(String s) {
    try {
        return of(Integer.parseInt(s));
    } catch (NumberFormatException e) {
        return empty();
    }
}

项目中

// pinpoint源码
private Optional<String> getAgentName(String agentId, long agentStartTime) {
    final int deltaTimeInMilli = 1000;
    final AgentInfo agentInfo = this.agentInfoService.getAgentInfoNoStatus(agentId, agentStartTime, deltaTimeInMilli);
    return agentInfo == null ? Optional.empty() : Optional.ofNullable(agentInfo.getAgentName());
}

public AlarmWriter(AlarmMessageSender alarmMessageSender, AlarmService alarmService, Optional<AlarmWriterInterceptor> alarmWriterInterceptor) {
    this.alarmMessageSender = Objects.requireNonNull(alarmMessageSender, "alarmMessageSender");
    this.alarmService = Objects.requireNonNull(alarmService, "alarmService");
    this.interceptor = alarmWriterInterceptor.orElseGet(DefaultAlarmWriterInterceptor::new);
}

Objects.requireNonNull(viewPointFilter, "viewPointFilter");
Objects.requireNonNull(serverMapDataFilter, "serverMapDataFilter").orElse(null);

JoinIntFieldBo min = joinIntFieldBoList.stream()
        .min(Comparator.comparing(JoinFieldBo::getMin))
        .orElseThrow(NoSuchElementException::new);

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值