Java

Java 8 Tutorial: Optional

Java 8 Tutorial: Optional

Optional là 1 container được giới thiệu trong Java 8, theo quảng cáo của Oracle thì nó sẽ giúp cho các lập trình viên tránh được cơn ác mộng NullPointerException. Nhưng theo mình thấy thì thay vì kiểm tra null bằng cách thông thường, Optional cung cấp 1 số hàm, tiện ích khác để chúng ta check null. Mình cũng chưa có cơ hội áp dụng vào thực tế, nhưng trước hết, cùng điểm qua 1 số hàm mà Optional cung cấp.

Optional chính xác là gì

Theo Javadoc của Java 8 định nghĩa thì:
Optional là một class nằm trong gói java.util, nó là 1 object container có thể đang chứa một object thực hoặc một giá trị null. Bằng việc sử dụng hàm isPresent có thể check được object được chứa trong nó là null hay không, và hàm get để get ra object đó.

empty
Tạo ra 1 empty Optional
// Create an empty Optional
Optional<String> emptyOptional = Optional.empty();

of

Hàm of nhận vào 1 đối tượng non-null và trả về một đối tượng Optional chứa đối tượng đó. Nếu truyền vào hàm of 1 đối tượng null, chúng ta nhận về 1 NullPointerException

// Create Optional object which contains a String object
Optional<String> name = Optional.of("Edward");
// It will throw NullPointerException
Optional<String> nullName = Optional.of(null);

ofNullable

Tương tự như of, nhưng trong trường hợp hàm nhận vào 1 null object, nó sẽ trả về 1 Empty Optional thay vì throw NullPointerException

// Return an empty Optional
Optional<String> empty = Optional.ofNullable(null);
// Simply check it. true
System.out.println(empty.equals(emptyOptional));

isPresent

Trả về true trong trường hợp object đang tồn tại. Ngược lại là false

// true
System.out.println(name.isPresent());
// false
System.out.println(emptyOptional.isPresent());

get

Trả về object nếu đang tồn tại trong Optional. Ngược lại throw NoSuchElementException

// Edward
System.out.println(name.get());
// It will throw NoSuchElementException
System.out.println(emptyOptional.get());

ifPresent

Hàm nhận vào 1 Consumer. Nếu object đang tồn tại sẽ chạy đoạn mã thực thi của Consumer, ngược lại sẽ không làm gì cả. Có thể hiểu nó là 1 Functional Interface nhận vào 1 tham số, không có giá trị trả về.

// Hello Edward
name.ifPresent((value) -> System.out.println("Hello " + value));
// Nothing happens
emptyOptional.ifPresent((value) -> System.out.println("Hello " + value));

orElse

Trả về object nếu đang tồn tại, ngược lại trả về giá trị mặc định do Client định nghĩa

// Edward
System.out.println(name.orElse("NoName"));
// NoName
System.out.println(emptyOptional.orElse("NoName"));

orElseGet

Phương thức này tương tự như phương thức orElse ở trên. Nhưng trong trường hợp object không tồn tại, nó sẽ dùng 1 Supplier interface để tạo ra giá trị mặc định. Supplier có thể được hiểu như một Functional Interface không nhận vào tham số nào, và có kiểu trả về.

// Edward
System.out.println(name.orElseGet(() -> {return "NoName";}));
// NoName
System.out.println(emptyOptional.orElseGet(() -> {return "NoName";}));

orElseThrow

Cũng tương tự như 2 phương thức orElse và orElseGet. Nhưng trong trường hợp object không tồn tại, sẽ throw ra Exception từ lambda expression do Client tự định nghĩa.

// Edward
try {
	System.out.println(name.orElseThrow(() -> {return new Exception();})); // Lambda expression
} catch (Exception e) {
	e.printStackTrace();
}
// Cannot get object from emptyOptional
try {
	// It will throw Exception
	System.out.println(emptyOptional.orElseThrow(Exception::new)); // Here is also lambda expression, both are the same
} catch (Exception e) {
	// Catch it
	System.out.println("Cannot get object from emptyOptional");
	e.printStackTrace();
}

map

Trong trường hợp object đang tồn tại, sử dụng lambda expression với đối số là object đó và tạo ra 1 Optional mới chứa object chính là kết quả trả về từ lambda expression. Ngược lại, trả về 1 empty Optional

Optional<String> upperName = name.map((value) -> {return value.toUpperCase();});
// EDWARD
upperName.ifPresent((value) -> {System.out.println(value);});
Optional<String> lowerName = emptyOptional.map((value) -> {return value.toUpperCase();});
// Nothing happens
lowerName.ifPresent((value) -> {System.out.println(value);});

flatMap

Phương thức này cũng nhận vào 1 lambda expression giống như phương thức map. Nhưng kết quả trả về từ lambda expression luôn là 1 Optional

// EDWARD
Optional<String> upperName2 = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName2.orElse("NoName"));
Optional<String> lowerName2 = emptyOptional.map((value) -> {return value.toUpperCase();});
// NoName
System.out.println(lowerName2.orElse("NoName"));

filter

Phương thức này truyền vào 1 lambda expresison thực thi cho Predicate interface như là một điều kiện kiểm tra. Nếu object đang chứa trong Optional không thỏa mãn sẽ trả về một empty Optional, ngược lại trả về một Optional mới chứa object đó.

Optional<String> validName = name.filter((value) -> {return value.length() > 3;});
// Edward is valid
validName.ifPresent((value) -> {System.out.println(value + " is valid");});
Optional<String> validName1 = name.filter((value) -> {return value.length() > 10;});
// Nothing happens
validName1.ifPresent((value) -> {System.out.println(value + " is valid");});

Các bạn có thể xem full source code ở đây:

import java.util.Optional;

public class Optional1 {

	public static void main(String[] args) {
		// empty
		// Create an empty Optional
		Optional<String> emptyOptional = Optional.empty();

		// of
		// Create Optional object which contains a String object
		Optional<String> name = Optional.of("Edward");
		try {
			// It will throw NullPointerException
			Optional<String> nullName = Optional.of(null);
		}
		catch(Exception e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		}

		// ofNullable
		// Return an empty Optional
		Optional<String> empty = Optional.ofNullable(null);
		// Simply check it. true
		System.out.println(empty.equals(Optional.empty()));

		// isPresent
		// true
		System.out.println(name.isPresent());
		// false
		System.out.println(emptyOptional.isPresent());

		// get
		// Edward
		System.out.println(name.get());
		try {
			// It will throw NoSuchElementException
			System.out.println(emptyOptional.get());
		}
		catch(Exception e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		}

		// ifPresent
		// Hello Edward
		name.ifPresent((value) -> System.out.println("Hello " + value));
		// Nothing happens
		emptyOptional.ifPresent((value) -> System.out.println("Hello " + value));

		// orElse
		// Edward
		System.out.println(name.orElse("NoName"));
		// NoName
		System.out.println(emptyOptional.orElse("NoName"));

		// orElseGet
		// Edward
		System.out.println(name.orElseGet(() -> {return "NoName";}));
		// NoName
		System.out.println(emptyOptional.orElseGet(() -> {return "NoName";}));

		// orElseThrow
		// Edward
		try {
			System.out.println(name.orElseThrow(() -> {return new Exception();})); // Lambda expression
		} catch (Exception e) {
			e.printStackTrace();
		}
		// Cannot get object from emptyOptional
		try {
			// It will throw Exception
			System.out.println(emptyOptional.orElseThrow(Exception::new)); // Here is also lambda expression, both are the same
		} catch (Exception e) {
			// Catch it
			System.out.println("Cannot get object from emptyOptional");
			e.printStackTrace();
		}

		// map
		Optional<String> upperName = name.map((value) -> {return value.toUpperCase();});
		// EDWARD
		upperName.ifPresent((value) -> {System.out.println(value);});
		Optional<String> lowerName = emptyOptional.map((value) -> {return value.toUpperCase();});
		// Nothing happens
		lowerName.ifPresent((value) -> {System.out.println(value);});

		// flatMap
		// EDWARD
		Optional<String> upperName2 = name.flatMap((value) -> Optional.of(value.toUpperCase()));
		System.out.println(upperName2.orElse("NoName"));
		Optional<String> lowerName2 = emptyOptional.map((value) -> {return value.toUpperCase();});
		// NoName
		System.out.println(lowerName2.orElse("NoName"));

		// filter
		Optional<String> validName = name.filter((value) -> {return value.length() > 3;});
		// Edward is valid
		validName.ifPresent((value) -> {System.out.println(value + " is valid");});
		Optional<String> validName1 = name.filter((value) -> {return value.length() > 10;});
		// Nothing happens
		validName1.ifPresent((value) -> {System.out.println(value + " is valid");});

	}

}

Kết quả:

null
java.lang.NullPointerException
	at java.util.Objects.requireNonNull(Unknown Source)
	at java.util.Optional.<init>(Unknown Source)
	at java.util.Optional.of(Unknown Source)
	at com.edward.tutorial.java8.optional.Optional1.main(Optional1.java:17)
true
true
false
Edward
No value present
java.util.NoSuchElementException: No value present
	at java.util.Optional.get(Unknown Source)
	at com.edward.tutorial.java8.optional.Optional1.main(Optional1.java:41)
Hello Edward
Edward
NoName
Edward
NoName
Edward
Cannot get object from emptyOptional
java.lang.Exception
	at com.edward.tutorial.java8.optional.Optional1$$Lambda$6/798154996.get(Unknown Source)
	at java.util.Optional.orElseThrow(Unknown Source)
	at com.edward.tutorial.java8.optional.Optional1.main(Optional1.java:76)
EDWARD
EDWARD
NoName
Edward is valid

Kết thúc bài giới thiệu về Optional trong Java 8 tại đây. Mình rất vui nếu có thể nhận được các đóng góp các ý kiến phản hồi bằng cách comment bên dưới bài viết. Các bạn có thể tham khảo một số bài viết khác trong series Java 8 Tutorial tại đây:
Series Java 8 Tutorial

http://www.edwardthienhoang.wordpress.com

Advertisements

2 thoughts on “Java 8 Tutorial: Optional”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s