问题 编写Java方法引用的简明方法?


给出了一些Java 8方法函数:

class Foo { Bar getBar() {} }
class Bar { Baz getBaz() {} }

两个访问器的组合看起来像:

Function<Foo, Bar> getBarFromFoo = Foo::getBar;
Function<Bar, Baz> getBazFromBar = Bar::getBaz;
Function<Foo, Baz> getBazFromFoo = getBarFromFoo.andThen(getBazFromBar);

有更简洁的方法吗?这似乎有效

((Function<Foo, Bar>) Foo::getBar).andThen(Bar::getBaz)

但它相当难看。由于优先原因,外围有意义,但为什么演员必要?

Foo::getBar::getBaz 会很好,但唉......)


10705
2017-08-18 15:37


起源

为什么不呢 foo -> foo.getBar()::getBaz?好像你过度复杂了。有什么我想念的吗? - Vince Emigh
@VinceEmigh,你的意思是 foo -> foo.getBar().getBaz()?否则,没有意义 - Andrew Tobilko
@VinceEmigh,你提出的方式不够灵活。准备简单的映射器要好得多(a->b, b->c用简单的操作组成它们 Function的 compose, andThen 在运行时而不是在编译时构建所有可能的情况。 - Andrew Tobilko
@AndrewTobilko如果灵活性是一个问题,你可以使用currying: foo -> bar -> bar::getBaz - Vince Emigh
@VinceEmigh,你无法使用 foo -> bar -> bar::getBaz 实现一个地图系列(在这里,得到一个 Baz 作为输出)。它可能会像......一样回归 Function<Foo, Function<Bar, Function<Bar, Baz>>> 这没有意义 - Andrew Tobilko


答案:


让我们定义一个功能界面:

@FunctionalInterface
interface MyFunctionalInterface {
    Bar getBar(Foo f);
}

我们可以简化方法参考 Foo::getBar 一点点,

(Foo foo) -> foo.getBar();

意思是 “拿一个 Foo 并返回一个 Bar。对于该描述,许多方法都是合适的(例如,我们的接口与 getBar 和a Funtion<Foo, Bar> 用它 apply):

MyFunctionalInterface f1 = (Foo foo) -> foo.getBar();
Function<Foo, Bar> f2 = (Foo foo) -> foo.getBar();

这就是为什么演员必要的问题的答案。


要回答肯定是否有更简洁的方式的问题,我们必须设定一个背景。上下文明确地给了我们一个 Function 继续合作:

class Functions {
    public static <I, O> Function<I, O> of(Function<I, O> function) {
        return function;
    }
}

Functions.of(Foo::getBar).andThen(Bar::getBaz);

10
2017-08-18 15:56





除了Java之外,没有专门用Java编写函数的方法 andThen()

你需要执行演员表,因为 Foo::getBar 很暧昧。 **它可以匹配具有类似方法签名的每个接口。

不幸, ((Function<Foo, Bar>) Foo::getBar).andThen(Bar::getBaz) 是你能做的最好的。


3
2017-08-18 15:56





也许只是使用lambda表达式?

x -> x.getBar().getBaz()

除了类型模糊之外,没有其他方法可以编写除已经建议的函数之外的函数。这甚至不会比这长得多 Foo::getBar::getBaz


1
2017-08-18 15:56





这是你将获得的最好的。如果您认为这样可行:

Foo::getBar::getBaz

它不会。那是因为 Foo::getBar 是一个 多元表达  - 这取决于使用的上下文 - 它可能是一个 Function,但也可能是一个 Predicate 例如;因此它可能适用于许多事情,所以演员阵容在那里是必要的。

你可以隐藏一个可以进行链接的方法 andThen,但问题仍然存在。

编辑

在这里查看示例:

public static void cool(Predicate<Integer> predicate) {

}

public static void cool(Function<Integer, String> function) {

}

和表达 cool(i -> "Test"); 将无法编译


0
2017-08-18 16:27



谢谢。我知道它不会起作用。只是希望...类型推断器应该知道它不是谓词,因为它不返回布尔值。我不是Java类型的推理向导,但似乎合乎逻辑的是,因为Foo :: getBar满足Foo-> Bar形式的任何功能接口并且getBaz满足Bar-> Baz,所以似乎是合理的 Foo::getBar::getBaz是一个多表达式,它满足Foo-> Baz形式的任何功能接口。 - Gene
@Gene Predicate 和 Function 只是一个例子来证明这些确实是多元表达式(参见编辑)......并且 type inference 对于lambdas和方法引用并不那么明显;这就是为什么这个链条根本没有实现的原因 - 它就是这样 到目前为止 不是微不足道的 - Eugene
我实际上写过一个类型推理器。我知道这不是微不足道的。这并不意味着它是不可能的。 - Gene
@Gene我不是说这不可能btw - 它可以在未来的idk版本中得到改进......就在此刻,我个人而言,(我绝对不是合适的人),看不到 真实的方式 安全地做。 - Eugene