Нови функции в Java 8

1. Общ преглед

В тази статия ще разгледаме набързо някои от най-интересните нови функции в Java 8.

Ще говорим за: интерфейс по подразбиране и статични методи, справка за метода и по избор.

Вече разгледахме някои функции на изданието на Java 8 - API на потока, ламбда изрази и функционални интерфейси - тъй като те са изчерпателни теми, които заслужават отделен поглед.

2. Интерфейс по подразбиране и статични методи

Преди Java 8 интерфейсите могат да имат само публични абстрактни методи. Не беше възможно да се добави нова функционалност към съществуващия интерфейс, без да се принудят всички изпълняващи класове да създадат реализация на новите методи, нито беше възможно да се създадат интерфейсни методи с реализация.

Започвайки с Java 8, интерфейсите могат да имат статични и стандартни методи, които, въпреки че са декларирани в интерфейс, имат определено поведение.

2.1. Статичен метод

Помислете за следния метод на интерфейса (нека наречем този интерфейс Vehicle ):

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

Методът на статичен производител () е достъпен само чрез и вътре в интерфейса. Не може да бъде заменен от прилагащ клас.

За да го извикате извън интерфейса, трябва да се използва стандартният подход за извикване на статичен метод:

String producer = Vehicle.producer();

2.2. Метод по подразбиране

Методите по подразбиране се декларират с помощта на новата ключова дума по подразбиране . Те са достъпни чрез екземпляра на изпълняващия клас и могат да бъдат заменени.

Нека добавим метод по подразбиране към нашия интерфейс Vehicle , който също ще извика статичния метод на този интерфейс:

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

Да приемем, че този интерфейс е реализиран от класа VehicleImpl. За изпълнение на метода по подразбиране трябва да се създаде екземпляр на този клас:

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

3. Референции на методите

Препратката към метода може да се използва като по-кратка и по-четлива алтернатива за ламбда израз, който извиква само съществуващ метод. Има четири варианта на препратки към методите.

3.1. Позоваване на статичен метод

Препратката към статичен метод съдържа следния синтаксис: ContainingClass :: methodName.

Нека се опитаме да преброим всички празни низове в списъка с помощта на Stream API.

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

Погледнете по-отблизо ламбда израза в метода anyMatch () , той просто извиква статичен метод isRealUser (потребител на потребител) от класа User . Така че може да бъде заменен с препратка към статичен метод:

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

Този тип код изглежда много по-информативен.

3.2. Позоваване на инстанционен метод

Препратката към метод на екземпляр съдържа следния синтаксис: c ontainingInstance :: methodName. Следният метод за извикване на код isLegalName (низ от низ) от тип User, който проверява входния параметър:

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

3.3. Позоваване на инстанционен метод на обект от определен тип

Този референтен метод приема следния синтаксис: C ontainingType :: methodName. Пример::

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

3.4. Препратка към конструктор

Препратката към конструктор приема следния синтаксис: ClassName :: new. Тъй като конструкторът в Java е специален метод, препратката към метода може да се приложи и към него с помощта на new като име на метод .

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

4. По желание

Преди Java 8 разработчиците трябваше внимателно да проверят стойностите, за които се позовават, поради възможността да хвърлят NullPointerException (NPE) . Всички тези проверки изискват доста досаден и склонен към грешки код на шаблон.

Класът Java 8 по избор може да помогне за справяне със ситуации, при които има възможност за получаване на NPE . Той работи като контейнер за обекта от тип T. Той може да върне стойност на този обект, ако тази стойност не е нула . Когато стойността в този контейнер е нула, това позволява извършването на някои предварително дефинирани действия, вместо да се хвърля NPE.

4.1. Създаване на незадължителен

Екземпляр на класа по избор може да бъде създаден с помощта на неговите статични методи:

Optional optional = Optional.empty();

Връща празен незадължителен.

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

Връща Незадължително, което съдържа ненулева стойност.

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.

И накрая, целият изходен код на статията е достъпен в GitHub.