How to missing values modeling
class Person {
private Car car;
public Car getCar() {
return car;
}
}
class Car {
private Insurance insurance;
public Insurance getInsurance() {
return insurance;
}
}
class Insurance {
private String name;
public String getName() {
return name;
}
}
public class Main {
public static String getCarInsuranceName(Person person) {
return person.getCar().getInsurance().getName();
}
}
- In general, if
Person
not Car
, it getCar()
is set to return null
, indicating that the value is missing. Therefore necessary to determine whether the return value null
to preventNullPointerException
Reducing defensive checks using NullPointerException
- Deep question: will increase the number of layers of code indentation, do not have the scalability, code maintenance difficult.
public static String getCarInsuranceName(Person person) {
if (person != null) {
Car car = person.getCar();
if (car != null) {
Insurance insurance = car.getInsurance();
if (insurance != null) {
return insurance.getName();
}
}
}
return "Unknown";
}
- Excessive exit statement: the number of exit points and more difficult to maintain.
public static String getCarInsuranceName(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();
}
problems caused by null
- Error source:
NullPointerException
is the Java development process in the most typical exception.
- Code bloat: deep nested need
null
to check, unreadable.
- Meaningless:
null
itself does not have any semantics, it is the wrong way to missing values modeling.
- Violation of philosophy: Java has been trying to avoid the process of staff aware of the existence of the pointer, but
null
the pointer exceptions.
- Type is missing:
null
do not belong to any type, which means it can be assigned to any variable, and when this variable is passed to the rest of the system will not be able to determine null
variable is initially what type.
Other alternatives are null languages
- Groovy: the introduction of safe navigation operator can safely access may be
null
variable, to avoid the throw NullPointerException
.
def carInsuranceName = person?.car?.insurance?.name
- Haskell:
Maybe
type, essentially optional
the package value.
- Scala:
Option[T]
, to explicitly call avaliable
the operation to check whether the variable has a value, in fact, a disguised form of null
checks.
Optional Class Profile
- Variable exists,
Optional
the type of the object is simply packaged. When the variable does not exist, the missing value is modeled as a "empty" Optional
objects, the method Optional.empty()
returns. Optional.empty()
The method is a static factory method, which returns Optional
a specific single instance of the class.
class Person {
private Car car;
public Optional<Car> getCar() {
return Optional.ofNullable(car);
}
}
class Car {
private Insurance insurance;
public Optional<Insurance> getInsurance() {
return Optional.ofNullable(insurance);
}
}
class Insurance {
private String name;
public String getName() {
return name;
}
}
Optional
And null
important semantic difference is that Optional
kind clearly demonstrates allowed to happen variable is missing, null
is not allowed.In the code, if the variable is not null
, then you do not need to add a null
check because null
checks will only cover up the problem, not really fix the problem.
- The above code portions corresponding to the book-shaped member when declaring such variables
private Optional<Car> car
, but this time a warning message IDEA 'Optional' used as field or parameter type
, reference StackOverFlow , Optional
as a member variable or function argument types can result in unnecessary additional packaging and unpacking logic, and can not be serialized, Thus only used at the return value Optional
.
Optional application of several models
Creating Optional objects
- An empty statement
Optional
Optional<Car> optCar = Optional.empty();
- Optional created based on a non-null value
Optional<Car> optCar = Optional.of(car)大专栏 Java8实战笔记0x06ass="token punctuation">;
- Acceptability
null
of Optional
, if passed null
then get an empty object
Optional<Car> optCar = Optional.ofNullable(car);
Optional objects extracted from the map using the converted value and
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);
- If a
Optional
contains a value, this value is then passed to the function as a parameter map
, the value will be converted. If Optional
empty, do nothing.
Optional use flatMap link objects
Optional<Person> optPerson = Optional.of(person);
- Note at the above code will not compile. The first
map
return value type Optional<Optional<Car>>
, not required Optional<Car>
.
- Should be used to solve the above problems
flatMap
, flatMap
the method takes a function as an argument, a return value of this function is another stream. This method will be applied to each elementary stream, forming a new stream.
public static String getCarInsuranceName(Person person) {
return Optional.ofNullable(person)
.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("Unknown");
}
The default behavior of objects and dereference Optional
get()
: If there is a variable return package variable values, otherwise throw an NoSuchElementException
exception. Not recommended.
orElse(T other)
: If the variable exists is returned, otherwise returns the passed default.
orElseGet(Supplier<? extends X> other)
: Is the orElse()
delay calling edition. Incoming Supplier
only called when the variable does not exist. If the object is to create a time-consuming operation should use this method.
orElseThrow(Supplier<? extends x> exceptionSupplier)
: Similar get()
, but errors are thrown by the incoming Supplier
creation.
ifPresent(Consumer<? extends T> consumer)
: When there is an execution variable passed Consumer
, or not do anything.
Optional combination of two objects
public Optional<Insurance> safeFind(Person person, Car car) {
return Optional.ofNullable(person)
.flatMap(
p -> Optional.ofNullable(car).map(c -> find(p, c))
);
}
- First,
c -> find(p, c)
return Insurance
, and the type of flow Optional
, it is not necessary flatMap
. After p -> Optional.ofNullable(car).map(c -> find(p, c))
get is Optional<Insurance>
, Optional.ofNullable(person)
the stream type is Optional<Person>
therefore required flatMap
.
Use filter excluding certain value
filter
The method accepts as a parameter a predicate. If the Optional
value of the object exists, and it is consistent with the predicate conditions, filter
the method returns its value; otherwise it returns an empty Optional
object.
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");
}
Optional examples of actual use of the
Optional package with a value may be null
Object value = map.get("key");
Optional<Object> value = Optional.ofNullable(map.get("key"));
Optional abnormal and contrast
public static Optional<Integer> stringToInt(String s) {
try {
return Optional.of(Integer.parseInt(s));
} catch (NumberFormatExcption e) {
return Optional.empty();
}
}
public OptionalInt stringToInt(String s) {
return Stream.ofNullable(s)
.filter(str -> str.matches("\d+"))
.mapToInt(Integer::parseInt)
.findAny();
}