Các tính năng mới trong Java 8

1. Khái quát chung

Trong bài viết này, chúng ta sẽ xem xét nhanh một số tính năng mới thú vị nhất trong Java 8.

Chúng ta sẽ nói về: giao diện mặc định và phương thức tĩnh, tham chiếu phương thức và Tùy chọn.

Chúng tôi đã trình bày một số tính năng của bản phát hành Java 8 - API luồng, biểu thức lambda và giao diện chức năng - vì chúng là các chủ đề toàn diện xứng đáng có một cái nhìn riêng.

2. Giao diện mặc định và phương thức tĩnh

Trước Java 8, các giao diện chỉ có thể có các phương thức trừu tượng công khai. Không thể thêm chức năng mới vào giao diện hiện có mà không buộc tất cả các lớp triển khai phải tạo một triển khai các phương thức mới, cũng như không thể tạo các phương thức giao diện với một triển khai.

Bắt đầu với Java 8, các giao diện có thể có các phương thức tĩnhmặc định , mặc dù được khai báo trong một giao diện, có một hành vi được xác định.

2.1. Phương pháp tĩnh

Hãy xem xét phương thức sau của giao diện (chúng ta hãy gọi giao diện này là Xe ):

static String producer() { return "N&F Vehicles"; }

Phương thức static producer () chỉ khả dụng thông qua và bên trong giao diện. Nó không thể bị ghi đè bởi một lớp triển khai.

Để gọi nó bên ngoài giao diện, cách tiếp cận tiêu chuẩn cho cuộc gọi phương thức tĩnh nên được sử dụng:

String producer = Vehicle.producer();

2.2. Phương pháp mặc định

Các phương thức mặc định được khai báo bằng từ khóa mặc định mới . Chúng có thể được truy cập thông qua phiên bản của lớp thực thi và có thể được ghi đè.

Hãy thêm một phương thức mặc định vào giao diện Xe của chúng ta, phương thức này cũng sẽ thực hiện lệnh gọi phương thức tĩnh của giao diện này:

default String getOverview() { return "ATV made by " + producer(); }

Giả sử rằng giao diện này được thực hiện bởi lớp VehicleImpl. Để thực thi phương thức mặc định, một thể hiện của lớp này phải được tạo:

Vehicle vehicle = new VehicleImpl(); String overview = vehicle.getOverview();

3. Tham khảo phương pháp

Tham chiếu phương thức có thể được sử dụng như một sự thay thế ngắn hơn và dễ đọc hơn cho biểu thức lambda chỉ gọi một phương thức hiện có. Có bốn biến thể của tham chiếu phương pháp.

3.1. Tham chiếu đến một phương pháp tĩnh

Tham chiếu đến một phương thức tĩnh có cú pháp sau: ContainsClass :: methodName.

Hãy thử đếm tất cả các chuỗi trống trong Danh sách với sự trợ giúp của API luồng.

boolean isReal = list.stream().anyMatch(u -> User.isRealUser(u));

Hãy xem xét kỹ hơn biểu thức lambda trong phương thức anyMatch () , nó chỉ thực hiện một cuộc gọi đến một phương thức tĩnh isRealUser (Người dùng người dùng) của lớp Người dùng . Vì vậy, nó có thể được thay thế bằng một tham chiếu đến một phương thức tĩnh:

boolean isReal = list.stream().anyMatch(User::isRealUser);

Loại mã này trông có nhiều thông tin hơn.

3.2. Tham chiếu đến một phương pháp phiên bản

Tham chiếu đến một phương thức thể hiện có cú pháp sau: c ontainingInstance :: methodName. Phương thức gọi mã sau isLegalName (Chuỗi chuỗi) thuộc loại Người dùng xác thực tham số đầu vào:

User user = new User(); boolean isLegalName = list.stream().anyMatch(user::isLegalName); 

3.3. Tham chiếu đến một phương pháp thực thể của một đối tượng của một loại cụ thể

Phương thức tham chiếu này có cú pháp sau: C ontainingType :: methodName. Một ví dụ::

long count = list.stream().filter(String::isEmpty).count();

3.4. Tham chiếu đến một Constructor

Tham chiếu đến một phương thức khởi tạo có cú pháp sau: ClassName :: new. Như constructor trong Java là một phương pháp đặc biệt, phương pháp tham khảo có thể được áp dụng cho nó quá với sự giúp đỡ của mới như một tên phương pháp .

Stream stream = list.stream().map(User::new);

4. Tùy chọn

Trước khi các nhà phát triển Java 8 phải xác thực cẩn thận các giá trị mà họ đề cập đến, vì có khả năng ném NullPointerException (NPE) . Tất cả những kiểm tra này đều yêu cầu một mã soạn sẵn khá khó chịu và dễ xảy ra lỗi.

Java 8 Lớp tùy chọn có thể giúp xử lý các tình huống có khả năng nhận được NPE . Nó hoạt động như một vùng chứa cho đối tượng kiểu T. Nó có thể trả về một giá trị của đối tượng này nếu giá trị này không phải là null . Khi giá trị bên trong vùng chứa này là null, nó cho phép thực hiện một số hành động được xác định trước thay vì ném NPE.

4.1. Tạo tùy chọn

Một thể hiện của lớp Tùy chọn có thể được tạo với sự trợ giúp của các phương thức tĩnh của nó:

Optional optional = Optional.empty();

Trả về tùy chọn trống .

String str = "value"; Optional optional = Optional.of(str);

Trả về một Tùy chọn có chứa giá trị khác rỗng.

Optional optional = Optional.ofNullable(getString());

Will return an Optional with a specific value or an empty Optional if the parameter is null.

4.2. Optional Usage

For example, you expect to get a List and in the case of null you want to substitute it with a new instance of an ArrayList. With pre-Java 8's code you need to do something like this:

List list = getList(); List listOpt = list != null ? list : new ArrayList();

With Java 8 the same functionality can be achieved with a much shorter code:

List listOpt = getList().orElseGet(() -> new ArrayList());

There is even more boilerplate code when you need to reach some object's field in the old way. Assume you have an object of type User which has a field of type Address with a field street of type String. And for some reason you need to return a value of the street field if some exist or a default value if street is null:

User user = getUser(); if (user != null) { Address address = user.getAddress(); if (address != null) { String street = address.getStreet(); if (street != null) { return street; } } } return "not specified";

This can be simplified with Optional:

Optional user = Optional.ofNullable(getUser()); String result = user .map(User::getAddress) .map(Address::getStreet) .orElse("not specified");

In this example we used the map() method to convert results of calling the getAdress() to the Optional and getStreet() to Optional. If any of these methods returned null the map() method would return an empty Optional.

Imagine that our getters return Optional. So, we should use the flatMap() method instead of the map():

Optional optionalUser = Optional.ofNullable(getOptionalUser()); String result = optionalUser .flatMap(OptionalUser::getAddress) .flatMap(OptionalAddress::getStreet) .orElse("not specified");

Another use case of Optional is changing NPE with another exception. So, as we did previously, let's try to do this in pre-Java 8's style:

String value = null; String result = ""; try { result = value.toUpperCase(); } catch (NullPointerException exception) { throw new CustomException(); }

And what if we use Optional? The answer is more readable and simpler:

String value = null; Optional valueOpt = Optional.ofNullable(value); String result = valueOpt.orElseThrow(CustomException::new).toUpperCase();

Notice, that how and for what purpose to use Optional in your app is a serious and controversial design decision, and explanation of its all pros and cons is out of the scope of this article. If you are interested, you can dig deeper, there are plenty of interesting articles on the Internet devoted to this problem. This one and this another one could be very helpful.

5. Conclusion

In this article, we are briefly discussing some interesting new features in Java 8.

There are of course many other additions and improvements which are spread across many Java 8 JDK packages and classes.

But, the information illustrated in this article is a good starting point for exploring and learning about some of these new features.

Cuối cùng, tất cả mã nguồn của bài viết đều có sẵn trên GitHub.