Null-Check & isPresent - 名称不同但问题相同?

Null-Check & isPresent - Different name but same issue?

我在Java中有以下代码:

public class Browser {

  public URL back() {
    try {
      //simulate: fetch last URL from Stack
      return Math.random() < 0.5 ? new URL("http://google.de") : null;
    } catch(MalformedURLException e) {
      return null;
    }
  }

  public void retrieveSite(URL url) {
    System.out.println(url);
    //...
  }

  public static void main(String[] args) {
    System.out.println("Normal back");
    Browser browser = new Browser();
    URL back = browser.back();
    if (back != null) browser.retrieveSite(back);
  }
}

我想了解有关 Optional 的更多信息并重新编写此代码,以便不再需要 return nullif (back!=null)

这就是我得到的:

public class Browser {

  Optional<URL> url = Optional.empty();

  public Optional<URL> back() {

    try {
      //simulate: fetch last URL from Stack
      if(Math.random()<0.5) {
        url = Optional.of(new URL("http://google.de"));
      } 
      return url;
    } catch(MalformedURLException e) {
      return url;
    }
  }

  public void retrieveSite(Optional<URL> url) {
    System.out.println(url);
    //...
  }

  public static void main(String[] args) {
    System.out.println("Normal back");
    Browser browser = new Browser();
    Optional<URL> back = browser.back();
    if(back.isPresent()) {
      browser.retrieveSite(back);
    }   
  }
}

现在为了避免将空 Optional 传递给 retrieveSite,我必须检查现值。但是我从检查 isPresent 而不仅仅是 !=null 中到底得到了什么?我应该 return 一个 default 值,这样我就可以摆脱 isPresent 吗?

我还必须更改 retrieveSite() 的参数以采用 Optional 这被认为是不好的做法?

提前致谢。

第二种方法略有不同的变体可以以更简洁的方式实现:

static class Browser {

    // good practice to return Optional instead of a null 
    Optional<URL> back() {
        try {
            //simulate: fetch last URL from Stack
            return Math.random() < 0.5 ? Optional.of(new URL("http://google.de")) : Optional.empty();
        } catch (MalformedURLException e) {
            return Optional.empty();
        }
    }

    // avoid using Optional as a parameter to a method
    static void retrieveSite(URL url) {
        System.out.println(url);
    }

    public static void main(String[] args) {
        System.out.println("Normal back");
        Browser browser = new Browser();
        // perform a void operation if the URL is present (consuming it)
        browser.back().ifPresent(Browser::retrieveSite);
    }
}

使用 Optional 你必须 unwrap/test 到 Optional 才能得到它,如果 Optional 没有被正确使用,你也会得到早期的空值异常(快速失败原则) .
例如:

  public static void main(String[] args) {
    System.out.println("Normal back");
    Browser browser = new Browser(); 
   // unwraping the optional or testing it is mandatory to get the object inside in
    browser.back().ifPresent(browser::retrieveSite); 
    // for example it will not compile
    browser.retrieveSite(browser.back()); 
    // of course you could cheat by invoking Optional.get() but that is a bad practice and the absence of object will be detected as soon as the invocation 
    browser.retrieveSite(browser.back().get()); 
  }

  public void retrieveSite(URL url) {
    //...
  }

如果没有 Optional,如果客户端忘记明确检查 no nullity (url != null),则可能会出现 NPE。对于 get/map 包装对象所必需的方法调用,此检查对于开发人员来说确实不那么引人注目。此外,如果 url 参数通过层传递,您可以稍后在最底层发现 null 引用,这使得问题可能更难以理解和解决:

  public static void main(String[] args) {
    System.out.println("Normal back");
    Browser browser = new Browser(); 
    // No unwrapping is necessary to get the url. 
    // So the robustness of the code depends on the developer habits
    browser.retrieveSite(browser.back());     
  }

  public void retrieveSite(URL url) {        
    //...
  }