【问题标题】:Why does IntToDoubleFunction not come with IntToDoubleFunction andThen(DoubleUnaryOperator after)为什么 IntToDoubleFunction 不附带 IntToDoubleFunction andThen(DoubleUnaryOperator after)
【发布时间】:2014-03-03 23:13:58
【问题描述】:

为什么DoubleUnaryOperator 带有默认的组合实现,比如

DoubleUnaryOperator andThen(DoubleUnaryOperator after);

IntToDoubleFunction 没有(从 b124 开始,假定功能完整)。是否有特殊原因 IntToDoubleFunction 没有

IntToDoubleFunction andThen(DoubleUnaryOperator after); ?

【问题讨论】:

  • 因为函数不是运算符?因为设计该规范的人认为他们的用例不需要方便复合?

标签: java java-8 functional-programming


【解决方案1】:

我只是偶然发现了这个问题 - 即许多函数式接口都有一个 andThen 方法,但其中许多(即主要处理原始类型的接口)没有 有这样的方法。事实上,我考虑过为这个问题提供赏金,因为目前的答案并不令人信服:一个函数可以有一个andThen 方法。事实上,引用类型的Function<T, R> 接口确实 有一个andThen 方法!但我找到了一个至少对我来说似乎很有说服力的原因....


在原始类型的...Function 接口中缺少andThen 方法并没有“明显”的原因。唯一真正令人信服的技术原因似乎是,由于返回和参数类型的特殊化,提供这样的方法将强加太多的组合。

考虑引用类型的Function<T, R> 接口,其中andThen 方法是这样实现的:

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

这允许调用站点的类型推断来解析任意类型的组合:

Function<String, Double> f = null;

Function<Double, Long>    g0 = null;
Function<Double, Integer> g1 = null;
Function<Double, Double>  g2 = null;
Function<Double, String>  g3 = null;

Function<String, Long>    c0 = f.andThen(g0); // works
Function<String, Integer> c1 = f.andThen(g1); // works
Function<String, Double>  c2 = f.andThen(g2); // works
Function<String, String>  c3 = f.andThen(g3); // works

例如,ToDoubleFunction 的类似组合如下:

ToDoubleFunction<String> f = null;

DoubleToLongFunction   g0 = null;
DoubleToIntFunction    g1 = null;
DoubleUnaryOperator    g2 = null;
DoubleFunction<String> g3 = null;

ToLongFunction<String>   c0 = f.andThen(g0); 
ToIntFunction<String>    c1 = f.andThen(g1);
ToDoubleFunction<String> c2 = f.andThen(g2);
Function<String, String> c3 = f.andThen(g3);

但这不能用单一的andThen 方法来解决。这些调用的每个参数和返回类型都是不同的。因此,由于这些类型的特殊化,这将需要 ToDoubleFunction 类中的四个 andThen 方法:

ToLongFunction<T> andThen(DoubleToLongFunction g) { ... }
ToIntFunction<T> andThen(DoubleToIntFunction g) { ... }
ToDoubleFunction<T> andThen(DoubleUnaryOperator g) { ... }
<V> Function<T, V> andThen(DoubleFunction<? super V> g) { ... }

(对于所有其他原始类型的特化,其数量相似)。

引入如此多的方法会导致 API 过度膨胀 - 特别是考虑到 lambda 表示法可以轻松模拟这些情况时:

ToLongFunction<String>   c0 = (t) -> g0.applyAsLong(f.applyAsDouble(t));
ToIntFunction<String>    c1 = (t) -> g1.applyAsInt(f.applyAsDouble(t));
ToDoubleFunction<String> c2 = (t) -> g2.applyAsDouble(f.applyAsDouble(t));
Function<String, String> c3 = (t) -> g3.apply(f.applyAsDouble(t));

【讨论】:

    【解决方案2】:

    javadocs 似乎对函数 (IntToDoubleFunction) 和一元运算符 (DoubleUnaryOperator) 进行了区分:

    Function

    表示接受一个参数并产生结果的函数。

    Unary Operator

    表示对单个操作数的操作,产生的结果为 与其操作数相同的类型。这是 Function 的专门化 操作数和结果类型相同的情况。

    因此,根据这一点,只有 UnaryOperator 能够拥有andThen(),因为参数和结果是相同的类型。对于函数,并不总是可以通过andThen() 链接,因为参数和结果可能不同。

    【讨论】:

      猜你喜欢
      • 2015-08-19
      • 2014-03-07
      • 2011-05-13
      • 2011-02-09
      • 1970-01-01
      • 2018-05-10
      • 2014-07-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多