Let's learn Java8 together (six) - Optional

The Optional class is a new class in Java8, which can effectively avoid null pointer exceptions.

The code of the Optional class is relatively simple and easy to understand. Its essence is an auxiliary class that wraps the object to be processed, and then uses the methods in Optional to effectively determine whether the processed object is empty. , so as to make a correct judgment.

Next we look at how to use Optional.

Create Optional

There are 3 ways to create an Optional:

  • Optional.empty() returns an empty Optional
  • Optional.of(object that is not null)
  • Optional.ofNullable (nullable object)

If you can ensure that the input parameter must not be null, you can use it Optional.of, because it Optional.ofwill determine whether it is null internally, and if it is null, an exception will be thrown.

If you are not sure whether the input parameter is null, you can use it Optional.ofNullable.

The object is created, let's see how to use it.

isPresent and ifPresent

The isPresent() method determines whether the processing object is null, and returns true if it is not null. The source code is as follows:

public boolean isPresent() {
    return value != null;
}

The ifPresent method has an input parameter ifPresent(Consumer<? super T> consumer), which means that if the object is not null, run the consumer for processing, which is similar to a callback function.

String s = "hello";		
Optional<String> optional = Optional.of(s);
if(optional.isPresent()) {
	System.out.println("the value is " + optional.get());
}

The same can be written as:

optional.ifPresent((val) -> {
	System.out.println("the value is " + val);
});

filter

The filter is to judge the processing object. If the judgment is true, it returns the current Optional. If it is false, it returns an empty Optional object. The source code is as follows:

public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if (!isPresent())
        return this;
    else
        return predicate.test(value) ? this : empty();
}

The filter method has a parameter: Predicate, which is a functional interface, so we can use Lambda expressions to handle it.

String s = "hello";		
Optional<String> optional = Optional.of(s);

boolean exist = optional
	.filter(val -> "hello1".equals(val))
	.isPresent();
System.out.println(exist); // false

map and flatMap

The role of the map method can be simply understood as taking other objects from the processing object and returning a new Optional. As shown in the following code:

public class OptionalMapTest {
	static class Goods {
		private String goodsName;
		private Company company;

	    ...getter setter
	}

	static class Company {
		private String companyName;

		...getter setter
	}

	public static void main(String[] args) {
		Company company = new Company();
		company.setCompanyName("Apple");
		Goods goods = new Goods();
		goods.setGoodsName("iphoneX");
		goods.setCompany(company);
		
		Optional<Goods> optional = Optional.of(goods);
		String companyName = optional
				// 从goods中取出Company,返回一个新的Optional<Company>
				.map(goodsObj -> goodsObj.getCompany())
				// 从company中取出companyName,返回一个新的Optional<String>
				.map(companyObj -> companyObj.getCompanyName())
				// 得到companyName
				.get();
		System.out.println(companyName);
	}
	
}

Under what circumstances should we use flatMap? Let's change the Company object in Goods to Optional<Company>.

static class Goods {
	private String goodsName;
	private Optional<Company> company;

	...getter setter
}

At this point, the following code will compile and report an error

String companyName = optional
	// 从goods中取出Company,返回一个新的Optional<Company>
	.map(goodsObj -> goodsObj.getCompany()) // !!这里会报错
	// 从company中取出companyName,返回一个新的Optional<String>
	.map(companyObj -> companyObj.getCompanyName())
	// 得到companyName
	.get();

Mainly this line of code optional.map(goodsObj -> goodsObj.getCompany()). Because it returns an Optional<Optional<Company>>object at this time.

And what we need is the Optional<Company>object. At this time, we should use flatMap, just optional.map(goodsObj -> goodsObj.getCompany())change it optional.flatMap(goodsObj -> goodsObj.getCompany())to.

String companyName = optional
	// 从goods中取出Company,返回一个新的Optional<Company>
	.flatMap(goodsObj -> goodsObj.getCompany())
	// 从company中取出companyName,返回一个新的Optional<String>
	.map(companyObj -> companyObj.getCompanyName())
	// 得到companyName
	.get();

A simple understanding is:

  • optional.map() will put the returned result into an Optional again
  • optional.flatMap() will not put the result into the Optional, leave this operation to the developer, and let the developer return an Optional

get,orElse,orElseGet,orElseThrow

  • get(): returns the processed value, or throws an exception if the value is empty
String s = null;
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.get()); // 抛出java.util.NoSuchElementException: No value present

There are several ways to deal with this situation

Method 1: UseisPresent()

String s = null;
Optional<String> optional = Optional.ofNullable(s);
if (optional.isPresent()) {
    System.out.println(optional.get());
} else {
    System.out.println("默认值");
}

Method 2: UseorElse(默认值)

String s = null;
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElse("默认值"));

orElse (default) means that if the value in Optional is null, return the given default value.

Method 3: UseorElseGet(Supplier)

String s = null;
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElseGet(() -> "默认值"));

orElse(Supplier) means that if the value in Optional is null, the specified Supplier interface is executed. Since Supplier is a functional interface, a Lambda expression can be used instead.

From this point of view, the processing of methods 2 and 3 is more elegant.

The difference between method 2 and method 3 is that method 3 can delay the return, and it will only be triggered when the value is null () -> "默认值", thus avoiding the generation of useless objects. Method 2 generates the "default value" string object anyway. The following example can illustrate:

String s = "1";
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElse(getDefault()));

Print:

生成了字符串对象
1

Even if the value in Optional is not null, getDefault() is still executed, which is completely unnecessary. Let's look at using orElseGet

String s = "1";
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElseGet(() -> getDefault()));

print: 1

Then look at orElseThrow, if the value is null, throw an exception directly

String s = null;
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElseThrow(() -> new NullPointerException("不能为空")));

Optional combat

{
	"user": {
		"age": 20
		,"name": "Jim"
		,"address": {
			"province": "浙江省"
			,"postcode": "111111"
		}
   }
}

Suppose there is such a json string, and now we want to get postcode information. If you don't use Optional, you need to write various if...else statements, and also determine whether the field exists.

String postcode = "unknown";
JSONObject user = jsonObj.getJSONObject("user");
if (user != null) {
    JSONObject address = user.getJSONObject("address");
    if (address != null) {
        String code = address.getString("postcode");
        if (postcode != null) {
            postcode = code;
        }
    }
}
System.out.println(postcode);

But with Optional you can write it like this:

JSONObject jsonObj = JSON.parseObject(json);

String postcode = Optional.ofNullable(jsonObj)
        .flatMap(jsonObject -> Optional.ofNullable(jsonObject.getJSONObject("user")))
        .flatMap(jsonObject -> Optional.ofNullable(jsonObject.getJSONObject("address")))
        .flatMap(jsonObject -> Optional.ofNullable(jsonObject.getString("postcode")))
        .orElse("unknown");

System.out.println(postcode);

Note that flatMap is used here, and an Optional object is returned manually by the developer, if map is used Optional<Optional<JSONObject>>.

The last sentence .orElse("unknown") means that if no value is found along the way, a default value is returned.

The advantage of Optional is to handle nested data structures like json data here. If the json data structure is not complete, the postcode field does not exist, or the entire address field does not exist, in the case where the existence of the value in the nested data cannot be guaranteed, using Optional is a good choice. It all ensures a correct return value.

subsection

This article mainly introduces the usage of the Optional class, and demonstrates how to use Optional to deal with nested data.

Regularly share technical dry goods, learn together, and progress together!

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324291070&siteId=291194637