构建 monad 时 Java 泛型的问题

Problems with Java Generics while building a monad

我正在研究 Java 代码以创建一个函数式风格的 monad,但我在使用泛型时感到震惊,如果我不这样做,Java 编译器会给我一个编译错误转换我的对象(尽管泛型可以解决这个问题!)

这是用法:

//COMPILATION ERROR! It requires a cast to String
String message = If.of("Hi", s->s!=null).apply(s->s+" guys!").get();

允许:

这是我的单子:

import java.util.function.Function;
import java.util.function.Predicate;

public class If<T, R> {

  private T t;
  private Predicate predicate;

  private If(T t, Predicate predicate) {
    this.t = t;
    this.predicate = predicate;
  }

  public static<T> If of(T t, Predicate predicate) {
    return new If(t, predicate);
  }

  public If<R,R> apply(Function<T, R> function) {
    if(predicate!=null && predicate.test(t)){
        return new If<R, R>(function.apply(t), null);
    }
    return If.of(this.t, null);
  }

  public T get() {
    return t;
  }

}

直接问题是 of 方法的 return 类型是原始的:

public static<T> If of(T t, Predicate predicate) {

您可能需要它是这样的:

public static<T> If<T, Something> of(T t, Predicate<T> predicate) {

我建议您不要真的想将 R 烘焙到 If 的类型中。如果改为在方法上声明它,则可以灵活地 apply 将其设置为您需要的任何类型:

public class If<T> {
  // ...

  public <R> If<R> apply(Function<T, R> function) {
    if(predicate!=null && predicate.test(t)){
      return new If<>(function.apply(t), null);
    }
    return If.of(this.t, null);
  }

  // ...
}

那么您的 of 签名可以是:

public static<T> If<T> of(T t, Predicate<T> predicate) {

如果您希望 API 更灵活一点,请添加通配符:

public static<T> If<T> of(T t, Predicate<? super T> predicate) {

public <R> If<R> apply(Function<? super T, ? extends R> function) {

Andy Turner 的回答直接解释了为什么您当前的代码无法编译,但您的 monad 似乎有一个更根本的问题 - 它不是很有用。

根据您的说法,如果条件为真,对 apply 的第一次调用应该 return 包装在 monad 中的转换对象,或者如果条件为真,则应该是包装在 monad 中的原始对象错误的。但是由于您将 null 作为两种情况的条件传递,因此对 apply 的任何后续调用都会导致达到第二个 return,因此总是 returning 结果第一次调用 apply.

事实上,不可能 return 原始对象或转换后的对象(无论如何都不是有用且类型安全的方式)。要以类型安全的方式执行此操作,您需要一个 Either<If<T>, If<R>> (假设存在这样的类型)。但是要从 Either 中提取值,您仍然需要一个 if 语句,这违背了 If<T> class.

的目的

显然,这只是练习编写 monad 的练习。既然如此,我建议你选择另一个monad来实现,比如Either。我也建议你先看看这个post