gpt4 book ai didi

java - 使用 Fugue/FunctionalJava 摆脱 Null 和 Throws?

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:15:04 24 4
gpt4 key购买 nike

好吧,我是一个长期的 OO 开发人员,试图进入这个“新发现的”函数式编程世界 - 至少,因为它与此有关,我正在尝试像 nullthrow 这样的代码通过玩弄 OptionEither monads,在 Java 领域并不存在。 (至少,我认为它们是单子(monad);我仍然对这个术语感到不自在,无论我是否正确使用它……)我要离开 Atlassian Fugue 库;我试着查看 Functional Java 库,它比我现在准备的要大得多。如果 fj 满足了我的需求而 Fugue 没有,我完全赞成。

基本上,我要完成的工作相当于这个相当丑陋的 Java 代码:

InputObject input = blah();

try {
final String message = getMessage(input);

if (message != null) {
try {
final ProcessedMessage processedMessage = processMessage(message);
if (processedMessage != null) {
try {
final ProcessedDetails details = getDetails(notificationDetails);
if (details != null) {
try {
awesomeStuff(details);
} catch (AwesomeStuffException ase) {
doSomethingWithAwesomeStuffException(ase);
}
}
} catch (GetDetailsException gde) {
doSomethingWithGetDetailsException(gde);
}
}
} catch (ProcessMessageException pme) {
doSomethingWithProcessMessageException(pme);
}
}
} catch (GetMessageException gme) {
doSomethingWithGetMessageException(gme);
}

天真地,使用 Either monad,我期望能够做这样的事情:

getMessage(input).fold(this::doSomethingWithGetMessageException, this::processMessage)
.fold(this::doSomethingWithProcessMessageException, this::getDetails)
.fold(this::doSomethingWithGetDetailsException, this::awesomeStuff)
.foldLeft(this::doSomethingWithAwesomeStuffException);

...其中每个逻辑方法都将返回一个 Either,其中包含适当的异常(可能是特定于域的错误类,但异常现在工作得很好)作为左侧,Option<something> 作为右侧. doSomethingWith... 方法将对错误执行他们想要执行的任何日志记录/处理。逻辑方法的一个例子是:

Either<GetMessageException, Option<String>> getMessage(final InputObject input) {
if (input == null) {
return Either.left(new GetMessageException("Input was null");
}

try {
return Either.right(loadMessage(input));
} catch (Exception ex) {
return Either.left(new GetMessageException(ex));
}
}

其他方法的定义类似;最好不要Option 作为参数 - 如果先前的方法返回 Option.none,则该方法根本不会被调用。

除了让我无法实际测试它的泛型类型的编译器错误巢穴之外,从逻辑上讲它看起来并不像它实际上做我想做的事 - 我期望事情基本上没有操作在返回第一个 Either.left 值后,但仔细观察它我猜它会继续尝试将 Either.left 结果传递到下一个调用并继续进行下去。

was 仅使用选项就可以实现我的目标:

getMessage(input).map(this::processMessage)
.map(this::getDetails)
.map(this::awesomeStuff);

不幸的是,要完成与原始 block 相同的逻辑,逻辑方法将与此类似地实现:

ProcessedMessage processMessage(final String message) {
try {
return doProcessing(message);
} catch (Exception ex) {
final GetMessageException gme = new GetMessageException(ex);
doSomethingWithGetMessageException(gme);
return null;
}
}

由于 .map 将结果提升为 Option ,因此在此处返回 null 工作正常。不幸的是,我基本上仍然对调用者隐藏了错误状态(更糟糕的是,IMO 使用 null 来表示错误 - 这并没有说明 doProcessing 是否出于潜在的正当理由返回 null)。

因此,从本质上讲,我喜欢具有与前面的 getMessage 示例一致的方法签名 -

Either<GetMessageException, Option<String>> getMessage(final InputObject input)

我喜欢这样的想法,即能够查看返回值并一眼就知道“此方法要么会失败,要么会返回一些可能存在但可能不存在的东西。”但是,我对 FP 太陌生了,不知道如何去做。

我的理解(尽管可能有缺陷)是我可以做类似的事情

getMessage(input).fold(this::doSomethingWithGetMessageException, this::processMessage)
.fold(this::doSomethingWithProcessMessageException, this::getDetails)
.fold(this::doSomethingWithGetDetailsException, this::awesomeStuff)
.foldLeft(this::doSomethingWithAwesomeStuffException);

(尽管可能使用不同的方法)将

  1. 在处理 Either.left 后随时停止处理(即调用适当的 doSomethingWithXException 方法并停止)
  2. 只要 Either.right 存在就继续执行整个链(如果相关函数返回 Option,则不存在 Option.none())。
  3. 在对 doSomethingWithAwesomeStuffException 的调用失败时调用 awesomeStuff - 如果任何其他方法失败,它将无法到达。

我尝试做的事情是否合理?我的意思是,我确信这是可能的,但是尝试将其硬塞进 Java 语法中是否太复杂了,甚至使用这些库中的任何一个(或我不知道的其他库)?

最佳答案

这肯定是一个自以为是的问题。你想要的是 flatMap 也被称为 monad 的绑定(bind)运算符。不幸的是,并不是所有的功能库都这样做(即 Guava 在其 Optional 上没有 flatMap)。 RxJavaReactor确实支持这种类型的操作,但这需要让您的服务更像流(我强烈推荐)。

据说在 Java 中执行这种逻辑存在很多问题,即 Java 没有任何模式匹配或任何 variants/ADTs/case classes .

由于上述原因以及大多数序列化程序在通用容器(即 Jackson)方面存在问题,大多数时候一个好的方法是使用行为 free 不可变的唯一类来返回结果(通常是静态内联类).

我在服务中一直这样做。即创建特定于特定服务请求的内联静态类。您可能会实现接口(interface)以跨内联类重用算法行为,但现实是 Java 对元组/变体/ADT/案例类的支持很糟糕(见上文)。

这通常(但并非总是)比在经常丢失数据的情况下抛出异常要好。

关于java - 使用 Fugue/FunctionalJava 摆脱 Null 和 Throws?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37037867/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com