Java 8 идиоматический, но не полностью верный, подход
Наиболее типичной вещью в Java 8 будет дополнительный класс:
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import java.util.Optional;
public class OptionalAsPartialFunction {
Optional<String> isEven(final int x) {
return Optional.of(x)
.filter(i -> i % 2 == 0)
.map(i -> i + " is even");
}
@Test
public void example() {
assertThat(isEven(10).get(), equalTo("10 is even"));
assertThat(isEven(11).isPresent(), is(false));
}
}
Если вы знакомы сНеобязательно, тогда вы увидите, что конкатенация строк i + " is even"
оценивается, только если условие фильтра i % 2 == 0
выполнено.Если вы не знакомы с Java Optional, вы также можете написать это с помощью if/else
:
Optional<String> isEven(final int x) {
if (x % 2 == 0) {
return Optional.of(x + " is even");
} else {
return Optional.empty();
}
}
Это должно сделать совершенно ясным, что конкатенация строк оценивается тогда и только тогда, когдаусловие защиты оценивается как истинное.
Более верный, но менее идиоматический (на Java) подход
При наивном подходе вы вызываете функцию и получаете Optional, который либо содержитфактическое значение функции или нет.Но функция PartialFunction в Scala немного сложнее.Из документации, на которую вы ссылались:
Даже если isDefinedAt возвращает true для a: A, вызов apply (a) может все равно вызвать исключение, поэтому следующий код допустим:
val f: PartialFunction[Int, Any] = { case _ => 1/0 }
Итак, мы хотели бы иметь возможность проверить, «определена» ли функция для входа, даже если попытаться вычислить, действительно ли для этого входа будет ошибка.
Поэтому более верным подходом было бы использование Optional<Supplier<...>>
.Внешний Optional
позволяет узнать, есть ли вычисление для выполнения, а внутренний Supplier
позволяет вам выполнить это вычисление (если вы выберете).Таким образом, пример будет выглядеть так:
Optional<Supplier<String>> isEven(final int x) {
return Optional.of(x)
.filter(i -> i % 2 == 0)
.map(i -> () -> i + " is even");
}
или с if/else
:
Optional<Supplier<String>> isEven(final int x) {
if (x % 2 == 0) {
return Optional.of(() -> x + " is even");
} else {
return Optional.empty();
}
}
и isPresent()
по-прежнему проверяется, определена ли функция, но get()
теперь будетвернуть Supplier
, чей метод get()
фактически вычислит значение:
@Test
public void example() {
assertThat("isPresent() checks whether the function is defined for the input", //
isEven(10).isPresent(), equalTo(true));
assertThat("get() returns the supplier that actually computes the value", //
isEven(10).get().get(), equalTo("10 is even"));
assertThat("isPresent() checks whether the function is defined for the input", //
isEven(11).isPresent(), is(false));
}