Статьи

Глубокое погружение в дополнительный класс API в Java 8

Все мы, программисты на Java, пережили ситуацию, когда мы вызываем метод для получения некоторого значения, а затем вместо непосредственного вызова некоторых методов для возвращаемого значения, мы сначала должны проверить, что возвращаемое значение не равно нулю, а затем вызвать методы на возвращаемое значение. Это было проблемой , которую пытались решить внешние API, такие как Guava . Также альтернативные языки JVM, такие как Scala, Ceylon и другие, имеют эти функции, встроенные прямо в основные API. В моем предыдущем посте я писал о поддержке одного из таких языков JVM, а именно Scala .

Более новая версия Java, т.е. Java 8, представляет новый класс под названием Optional . Класс Javadoc for Optional говорит:

Контейнерный объект, который может содержать или не содержать ненулевое значение. Если значение присутствует, isPresent () вернет true, а get () вернет значение.

В этом посте давайте рассмотрим каждый из методов, представленных в классе Optional, и объясним то же самое с помощью одного или двух примеров.

of

Возвращает Optional с указанным текущим ненулевым значением.

Этот метод является фабричным методом для создания экземпляров класса Optional. Здесь следует обратить внимание на то, что значение, передаваемое созданному экземпляру, должно быть ненулевым. Если переданное значение является нулевым, тогда NullPointerException .

1
2
3
4
//Creating an instance of Optional using the factory method.
Optional<String> name = Optional.of("Sanaulla");
//This fails with a NullPointerException.
Optional<String> someNull = Optional.of(null);

ofNullable

Возвращает Optional, описывающий указанное значение, если оно не равно NULL, в противном случае возвращает пустой Optional.

Как и of методе, единственное отличие состоит в том, что этот метод также обрабатывает нулевые значения. Пример:

1
2
3
//This represents an instance of Optional containing no value
//i.e the value is 'null'
Optional empty = Optional.ofNullable(null);

isPresent

Очень просто понять:

Вернуть true, если присутствует значение, иначе false.

Что-то типа:

1
2
3
4
5
6
7
//isPresent method is used to check if there is any
//value embedded within the Optional instance.
if (name.isPresent()) {
  //Invoking get method returns the value present
  //within the Optaional instance.
  System.out.println(name.get());//prints Sanaulla
}

get

Если значение присутствует в этом Необязательном, возвращает значение, в противном случае генерируется исключение NoSuchElementException.

Этот метод используется для получения значения, присутствующего в дополнительном экземпляре. Мы видели один такой пример выше. Давайте посмотрим на пример, где NoSuchElementException :

1
2
3
4
5
6
7
8
//The below code prints: No value present
try {
  //Invoking get method on an empty Optaional instance
  //throws NoSuchElementException.
  System.out.println(empty.get());
} catch (NoSuchElementException ex) {
  System.out.println(ex.getMessage());
}

ifPresent

Если значение присутствует, вызовите указанного потребителя со значением, в противном случае ничего не делайте.

Чтобы понять этот метод, вы должны понимать класс Consumer . Короче говоря, Consumer – это класс с единственным абстрактным методом, который потребляет некоторое значение и выполняет с ним некоторую операцию, не возвращая никакого значения. В Java 8 можно передать лямбда-выражение методу, ожидающему реализацию интерфейса Consumer.
Вышеупомянутый метод принимает блок кода / лямбда-выражения для выполнения некоторой операции, если значение присутствует в дополнительном экземпляре. Что-то типа:

1
2
3
4
5
6
//ifPresent method takes a lambda expression as a parameter.
//The lambda expression can then consume the value if it is present
//and perform some operation with it.
name.ifPresent((value) -> {
  System.out.println("The length of the value is: " + value.length());
});

orElse

Вернуть значение, если оно присутствует, в противном случае вернуть другое.

Этот метод либо возвращает значение, присутствующее в дополнительном экземпляре, и, если нет, возвращает значение, переданное в качестве параметра в метод orElse . Давайте посмотрим на пример:

1
2
3
4
5
6
//orElse method either returns the value present in the Optional instance
//or returns the message passed to the method in case the value is null.
//prints: There is no value present!
System.out.println(empty.orElse("There is no value present!"));
//prints: Sanaulla
System.out.println(name.orElse("There is some value!"));

orElseGet

Этот метод аналогичен вышеуказанному. Разница в том, как получить значение по умолчанию. В методе orElse мы передаем фиксированную строку в качестве значения по умолчанию, но в методе orElseGet мы передаем реализацию интерфейса поставщика, у которого есть метод, который используется для генерации значения по умолчанию. Давайте посмотрим на пример:

1
2
3
4
5
6
7
//orElseGet is similar to orElse with a difference that instead of passing
//a default value, we pass in a lambda expression which generates the default
//value for us.
//prints: Default Value
System.out.println(empty.orElseGet(() -> "Default Value"));
//prints: Sanaulla
System.out.println(name.orElseGet(() -> "Default Value"));

orElseThrow

Возвратите содержащееся значение, если оно присутствует, в противном случае выведите исключение, которое будет создано предоставленным поставщиком.

Как и в методе orElseGet мы передаем интерфейс поставщика , но в методе orElseThrow мы передаем лямбда-выражение / ссылку на метод, чтобы orElseThrow исключение, когда значение не найдено. Пример для этого:

1
2
3
4
5
6
7
8
9
try {
  //orElseThrow similar to orElse method, instead of returning a default
  //value, this method throws an exception which is generated from
  //the lambda expression/method reference passed as a param to the method.
  empty.orElseThrow(ValueAbsentException::new);
} catch (Throwable ex) {
  //prints: No value present in the Optional instance
  System.out.println(ex.getMessage());
}

И определение для ValueAbsentException:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
class ValueAbsentException extends Throwable {
 
  public ValueAbsentException() {
    super();
  }
 
  public ValueAbsentException(String msg) {
    super(msg);
  }
 
  @Override
  public String getMessage() {
    return "No value present in the Optional instance";
  }
}

map

Из документации по методу карты:

Если значение присутствует, примените к нему предоставленную функцию сопоставления, а если результат не равен нулю, верните необязательный параметр, описывающий результат. В противном случае верните пустой необязательный.

Этот метод используется для применения набора операций к значению, присутствующему в дополнительном экземпляре. Набор операций передается в форме лямбда-выражения, представляющего реализацию интерфейса Function. Если вы не знакомы с интерфейсом Function, пожалуйста, потратьте некоторое время на чтение моего предыдущего сообщения в блоге на ту же тему здесь . Давайте посмотрим на пример для метода map :

1
2
3
4
5
6
//map method modifies the value present within the Optional instance
//by applying the lambda expression passed as a parameter.
//The return value of the lambda expression is then wrapped into another
//Optional instance.
Optional<String> upperName = name.map((value) -> value.toUpperCase());
System.out.println(upperName.orElse("No value found"));

flatMap

Из документации по методу flatMap :

Если значение присутствует, примените к нему предоставленную функцию отображения Optional-Bear, верните этот результат, в противном случае верните пустой Optional. Этот метод аналогичен map (Function), но предоставленный сопоставитель является тем, результатом которого уже является Optional, и при вызове flatMap не оборачивает его дополнительным Optional.

Этот метод очень похож на метод map и отличается от него типом возвращаемой функции отображения. В случае метода карты возвращаемое значение функции отображения может быть любого типа T , где, как и в случае метода flatMap возвращаемое значение функции отображения может иметь только тип Optional.

Давайте рассмотрим приведенный выше пример flatMap метода flatMap для метода flatMap :

1
2
3
4
5
6
7
8
//flatMap is exactly similar to map function, the differece being in the
//return type of the lambda expression passed to the method.
//In the map method, the return type of the lambda expression can be anything
//but the value is wrapped within an instance of Optional class before it
//is returned from the map method, but in the flatMap method the return
//type of lambda expression's is always an instance of Optional.
upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));//prints SANAULLA

filter

Этот метод используется для ограничения значения в Optional экземпляре путем передачи условия, которое будет применено к значению, в метод filter . Документация гласит:

Если значение присутствует, и значение соответствует данному предикату, вернуть Optional, описывающий значение, в противном случае вернуть пустой Optional.

К настоящему времени у вас должно быть представление о том, как передать некоторый блок кода методу. Да, это лямбда-выражение. В случае этого метода мы должны передать лямбда-выражение, которое будет реализацией интерфейса Predicate. Если вы не знакомы с интерфейсом Predicate, прочитайте этот пост.

Теперь давайте рассмотрим различные способы использования метода filter т.е. оба примера выполнения условия и выполнения условия не выполняются.

01
02
03
04
05
06
07
08
09
10
11
12
//filter method is used to check if the given optional value satifies
//some condtion. If it satifies the condition then the same Optional instance
//is returned, otherwise an empty Optional instance is returned.
Optional<String> longName = name.filter((value) -> value.length() > 6);
System.out.println(longName.orElse("The name is less than 6 characters"));//prints Sanaulla
 
//Another example where the value fails the condition passed to the
//filter method.
Optional<String> anotherName = Optional.of("Sana");
Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
//prints: The name is less than 6 characters
System.out.println(shortName.orElse("The name is less than 6 characters"));

С этим я познакомил вас с различными методами, присутствующими в классе Optional . Позвольте мне объединить все вышеупомянутые примеры в один единственный пример, показанный ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
public class OptionalDemo {
 
  public static void main(String[] args) {
    //Creating an instance of Optional
    //This value can also be returned from some method.
    Optional<String> name = Optional.of("Sanaulla");
 
    //This represents an instance of Optional containing no value
    //i.e the value is 'null'
    Optional empty = Optional.ofNullable(null);
 
    //isPresent method is used to check if there is any
    //value embedded within the Optional instance.
    if (name.isPresent()) {
      //Invoking get method returns the value present
      //within the Optaional instance.
      System.out.println(name.get());
 
    }
 
    try {
      //Invoking get method on an empty Optaional instance
      //throws NoSuchElementException.
      System.out.println(empty.get());
    } catch (NoSuchElementException ex) {
      System.out.println(ex.getMessage());
    }
 
    //ifPresent method takes a lambda expression as a parameter.
    //The lambda expression can then consume the value if it is present
    //and perform some operation with it.
    name.ifPresent((value) -> {
      System.out.println("The length of the value is: " + value.length());
    });
 
    //orElse method either returns the value present in the Optional instance
    //or returns the message passed to the method in case the value is null.
    System.out.println(empty.orElse("There is no value present!"));
    System.out.println(name.orElse("There is some value!"));
 
    //orElseGet is similar to orElse with a difference that instead of passing
    //a default value, we pass in a lambda expression which generates the default
    //value for us.
    System.out.println(empty.orElseGet(() -> "Default Value"));
    System.out.println(name.orElseGet(() -> "Default Value"));
 
    try {
      //orElseThrow similar to orElse method, instead of returning a default
      //value, this method throws an exception which is genereated from
      //the lambda expression/method reference passed as a param to the method.
      empty.orElseThrow(ValueAbsentException::new);
    } catch (Throwable ex) {
      System.out.println(ex.getMessage());
    }
 
    //map method modifies the value present within the Optional instance
    //by applying the lambda expression passed as a parameter.
    //The return value of the lambda expression is then wrapped into another
    //Optional instance.
    Optional<String> upperName = name.map((value) -> value.toUpperCase());
    System.out.println(upperName.orElse("No value found"));
 
    //flatMap is exactly similar to map function, the differece being in the
    //return type of the lambda expression passed to the method.
    //In the map method, the return type of the lambda expression can be anything
    //but the value is wrapped within an instance of Optional class before it
    //is returned from the map method, but in the flatMap method the return
    //type of lambda expression's is always an instance of Optional.
    upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
    System.out.println(upperName.orElse("No value found"));
 
    //filter method is used to check if the given optional value satifies
    //some condtion. If it satifies the condition then the same Optional instance
    //is returned, otherwise an empty Optional instance is returned.
    Optional<String> longName = name.filter((value) -> value.length() > 6);
    System.out.println(longName.orElse("The name is less than 6 characters"));
 
    //Another example where the value fails the condition passed to the
    //filter method.
    Optional<String> anotherName = Optional.of("Sana");
    Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
    System.out.println(shortName.orElse("The name is less than 6 characters"));
 
  }
 
}

И вывод приведенного выше кода:

01
02
03
04
05
06
07
08
09
10
11
12
Sanaulla
No value present
The length of the value is: 8
There is no value present!
Sanaulla
Default Value
Sanaulla
No value present in the Optional instance
SANAULLA
SANAULLA
Sanaulla
The name is less than 6 characters