Почему использование Optional вместо Objects.isNull или obj == null дает мне преимущество?

Я хочу создать метод, который будет использовать дополнительные функции и возвращать значение NodeId.
Это значение я должен извлечь из объекта Asset.
В некоторых случаях я уже использую некоторые функции, такие как ifPresent, filter, flatMap. Но теперь я хочу четко понять, могу ли я использовать Необязательно с простыми методами, как в примере ниже, где мне нужно просто извлечь значение из другого объекта

Первый пример предположительно не очень хороший, но, тем не менее, я попробуйте использовать необязательно:

   public Optional<NodeId> findParentNodeIdByAsset(Asset asset) {
        Optional<Asset> tmpAsset = Optional.ofNullable(asset);
        if(tmpAsset.isEmpty()) {
            throw new NullPointerException();
        }
        return Optional.ofNullable(tmpAsset.get().getParents().iterator().next());
    }


Во втором примере я пытаюсь написать то же самое, но без Optional:

    public NodeId tmpFindParentNodeIdByAsset(Asset asset) {
        if(Objects.isNull(asset)) {
            throw new NullPointerException();
        }
        return asset.getParents().iterator().next();
    }

person blizardinka    schedule 15.10.2019    source источник
comment
Я уверен, что Optional сделает API чище и лучше, чем ... ожидание исключения NullPointerException. Это при условии, что вы правильно используете Optional, чего, ИМО, вы не являетесь. Я бы просто return Optional.ofNullable(asset).map(asset -> asset.getParents().iterator().next());. В этом суть Optional: вызывающий проверит, пуста ли она перед ее использованием.   -  person ernest_k    schedule 15.10.2019


Ответы (4)


Нет смысла проверять null asset или пустой tmpAsset, если вы собираетесь бросить NullPointerException в таких случаях.

Просто пиши:

public NodeId tmpFindParentNodeIdByAsset(Asset asset) {
    return asset.getParents().iterator().next();
}

и NullPointerException будет брошен, если вы попытаетесь отменить ссылку на нулевую ссылку.

Теперь использование Optional становится полезным, если вы не хотите бросать NullPointerException или если asset не единственная ссылка, которая может быть null.

Например, предположим, что asset.getParents() также может быть null, и в случае, если asset или asset.getParents() равны нулю, вы хотите вернуть какое-то значение по умолчанию или пустой Optional.

Вы можете связать несколько map() вызовов, чтобы преобразовать каждую потенциально null ссылку в следующую потенциально null ссылку, и закончить либо Optional (как в примере ниже), значением по умолчанию или исключением.

public Optional<NodeId> findParentNodeIdByAsset(Asset asset) {
    return Optional.ofNullable(asset)
                   .map(asset -> asset.getParents())
                   .map(parents -> parents.iterator().next());
}

Кроме того, может быть безопаснее проверить, что parents не пуст, прежде чем пытаться получить первый элемент его Iterator.

person Eran    schedule 15.10.2019
comment
В этом ответе подразумевается, что он становится полезным, когда у вас есть цепочка функциональных элементов. Вы можете объединить несколько функций .map () в один оператор и обработать любое условие null / failure / not-found в одном месте. - person AutomatedMike; 15.10.2019
comment
@AutomatedMike: да, это то, что я имел в виду в if asset is not the only reference that may be null, хотя ваш комментарий более ясен. - person Eran; 15.10.2019

Вы не совсем правильно используете Optional в своем первом методе, не имеет большого смысла бросать NullPointerException в метод, в который вы возвращаете Optional. См. Ответ Эрана для правильного использования.

Если вы, однако, действительно хотите выдать NullPointerException, когда ввод - null, то вместо этого используйте это:

public NodeId tmpFindParentNodeIdByAsset(Asset asset) {
    Objects.requireNonNull(asset, "asset");
    return asset.getParents().iterator().next();
}
person xtratic    schedule 15.10.2019

Используя Optional, вы гарантируете, что вызывающий абонент знает, что возвращаемое значение может быть нулевым.

person alex.parej    schedule 15.10.2019

Использование Optional делает код более плавным и улучшает его читаемость. В вашем случае я бы действительно использовал Optional. Например:

public NodeId tmpFindParentNodeIdByAsset(Asset asset) {
  return Optional.ofNullable(asset)
    .map(asset -> asset.getParents().iterator().next())
    .orElseThrow(UnsupportedOperationException::new)
}

В противном случае, если вы хотите вернуть необязательный параметр, просто удалите orElseThrow (). Для каждого метода getParents (), iterator () или next (), который может возвращать null, вы должны создать цепочку карт, чтобы не попасть в NPE. Например:

public Optional<NodeId> tmpFindParentNodeIdByAsset(Asset asset) {
  return Optional.ofNullable(asset)
    .map(asset -> asset.getParents())
    .map(parents -> parents.iterator().next());
}
person Joel    schedule 15.10.2019