对于许多开发人员来说,Java 8中引入的可选类型是一个新事物。

是一个getter方法返回可选的<Foo>类型的地方,经典的Foo一个很好的实践?假设该值可以为空。


我想说,一般来说,对于返回值可以为空的可选类型是个好主意。然而,对于框架,我认为用可选类型替换经典的getter方法会在使用依赖于getter和setter编码约定的框架(例如Hibernate)时带来很多麻烦。


在我自己做了一些研究之后,我发现了一些事情,可以建议什么时候这样做合适。最权威的是以下引用自Oracle文章:

需要注意的是,Optional类的目的不是替换每一个空引用。相反,它的目的是帮助设计更容易理解的api,这样通过读取方法的签名,就可以判断是否可以获得可选值。这迫使您主动展开Optional以处理缺少值的情况。”-厌倦了空指针异常?考虑使用Java SE 8的Optional!

我还从Java 8 Optional中找到了这个节选:如何使用它

“Optional并不适用于这种情况,因为它并不能为我们买到任何东西: 在领域模型层(不可序列化) 在dto中(同样的原因) 输入方法的参数 在构造函数参数中

这似乎也提出了一些有效的观点。

我没有发现任何消极的含义或危险信号,表明“Optional”应该避免。我认为一般的想法是,如果它有帮助或提高了API的可用性,就使用它。


当然,人们会做他们想做的事。但在添加这个功能时,我们确实有一个明确的意图,它不是一个通用的Maybe类型,尽管很多人都希望我们这样做。我们的意图是为库方法返回类型提供一种有限的机制,其中需要一种明确的方式来表示“没有结果”,而使用null极有可能导致错误。

例如,你可能永远不应该将它用于返回结果数组或结果列表的事情;而是返回一个空数组或列表。几乎不应该将它用作某个字段或方法参数。

我认为常规地使用它作为getter的返回值肯定是过度使用的。

可选选项没有错,应该避免它,只是它不是很多人希望的那样,因此我们相当担心过度使用的风险。

(Public service announcement: NEVER call Optional.get unless you can prove it will never be null; instead use one of the safe methods like orElse or ifPresent. In retrospect, we should have called get something like getOrElseThrowNoSuchElementException or something that made it far clearer that this was a highly dangerous method that undermined the whole purpose of Optional in the first place. Lesson learned. (UPDATE: Java 10 has Optional.orElseThrow(), which is semantically equivalent to get(), but whose name is more appropriate.))


如果你正在使用现代序列化器和其他理解Optional的框架,那么我发现这些指导原则在编写实体bean和域层时很有效:

If the serialization layer (usually a DB) allows a null value for a cell in column BAR in table FOO, then the getter Foo.getBar() can return Optional indicating to the developer that this value may reasonably be expected to be null and they should handle this. If the DB guarantees the value will not be null then the getter should not wrap this in an Optional. Foo.bar should be private and not be Optional. There's really no reason for it to be Optional if it is private. The setter Foo.setBar(String bar) should take the type of bar and not Optional. If it's OK to use a null argument then state this in the JavaDoc comment. If it's not OK to use null an IllegalArgumentException or some appropriate business logic is, IMHO, more appropriate. Constructors don't need Optional arguments (for reasons similar to point 3). Generally I only include arguments in the constructor that must be non-null in the serialization database.

为了使上面的更有效,你可能想要编辑你的IDE模板来生成getter和toString(), equals(Obj o)等的相应模板,或者直接使用这些字段(大多数IDE生成器已经处理空值)。


将Optional添加到Java的原因是:

return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .findFirst()
    .getOrThrow(() -> new InternalError(...));

比这个更干净:

Method matching =
    Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .getFirst();
if (matching == null)
  throw new InternalError("Enclosing method not found");
return matching;

我的观点是,Optional被编写来支持函数式编程,它同时被添加到Java中。(这个例子来自Brian Goetz的博客。更好的示例可能是使用orElse()方法,因为这段代码无论如何都会抛出异常,但您已经了解了情况。)

But now, people are using Optional for a very different reason. They're using it to address a flaw in the language design. The flaw is this: There's no way to specify which of an API's parameters and return values are allowed to be null. It may be mentioned in the javadocs, but most developers don't even write javadocs for their code, and not many will check the javadocs as they write. So this leads to a lot of code that always checks for null values before using them, even though they often can't possibly be null because they were already validated repeatedly nine or ten times up the call stack.

I think there was a real thirst to solve this flaw, because so many people who saw the new Optional class assumed its purpose was to add clarity to APIs. Which is why people ask questions like "should getters return Optionals?" No, they probably shouldn't, unless you expect the getter to be used in functional programming, which is very unlikely. In fact, if you look at where Optional is used in the Java API, it's mainly in the Stream classes, which are the core of functional programming. (I haven't checked very thoroughly, but the Stream classes might be the only place they're used.)

如果您确实计划在一些函数代码中使用getter,那么使用一个标准getter和返回Optional的第二个getter可能是个好主意。

哦,如果你需要你的类是可序列化的,你绝对不应该使用Optional。

对于API缺陷来说,可选选项是一个非常糟糕的解决方案,因为a)它们非常冗长,b)它们从一开始就没有打算解决这个问题。

A much better solution to the API flaw is the Nullness Checker. This is an annotation processor that lets you specify which parameters and return values are allowed to be null by annotating them with @Nullable. This way, the compiler can scan the code and figure out if a value that can actually be null is being passed to a value where null is not allowed. By default, it assumes nothing is allowed to be null unless it's annotated so. This way, you don't have to worry about null values. Passing a null value to a parameter will result in a compiler error. Testing an object for null that can't be null produces a compiler warning. The effect of this is to change NullPointerException from a runtime error to a compile-time error.

这改变了一切。

As for your getters, don't use Optional. And try to design your classes so none of the members can possibly be null. And maybe try adding the Nullness Checker to your project and declaring your getters and setter parameters @Nullable if they need it. I've only done this with new projects. It probably produces a lot of warnings in existing projects written with lots of superfluous tests for null, so it might be tough to retrofit. But it will also catch a lot of bugs. I love it. My code is much cleaner and more reliable because of it.

(还有一种新的语言可以解决这个问题。Kotlin可以编译为Java字节代码,它允许您在声明对象时指定对象是否可以为空。这是一种更干净的方法。)

原文补遗(第二版)

经过一番思考后,我勉强得出结论:在一个条件下返回Optional是可以接受的:检索到的值实际上可能是null。我见过很多代码,人们例行地从getter返回Optional,而这些getter不可能返回null。我认为这是一种非常糟糕的编码实践,只会增加代码的复杂性,从而更容易产生错误。但是当返回值实际上可能为空时,请继续并将其包装在Optional中。

请记住,为函数式编程设计的方法,以及需要函数引用的方法,将(并且应该)以两种形式编写,其中一种使用Optional。例如,option .map()和option . flatmap()都接受函数引用。第一个参数接受一个对普通getter的引用,第二个参数接受一个返回Optional的getter。因此,返回值不能为null的Optional并不是对任何人都有帮助。

说了这么多,我仍然认为Nullness Checker使用的方法是处理null的最佳方法,因为它们将nullpointerexception从运行时错误转变为编译时错误。


您必须记住,经常被引用的建议来自那些在Java之外几乎没有任何经验的人,他们没有选项类型或函数式编程经验。

所以你要半信半疑。相反,让我们从“良好实践”的角度来看:

好的实践不仅意味着问“我们如何编写新代码?”,还意味着问“现有代码会发生什么?”

在Optional的例子中,我的环境找到了一个良好且易于遵循的答案:

Optional用于在记录中指定可选值: 记录宠物(字符串名称,可选<品种>品种, 可选< ZonedDateTime > dateOfBirth)

这意味着现有的代码是很好的,但是使用记录的代码(即“新代码”)会导致可选的广泛采用。

结果在可读性和可靠性方面取得了圆满的成功。只要停止使用null。