【问题标题】:How to force lambda type to a specific one?如何强制 lambda 类型为特定类型?
【发布时间】:2018-11-29 20:04:32
【问题描述】:

假设我有一个带有这样签名的函数:

public static <T> List<Future<T>> invokeAll(Stream<Callable<T>> tasks) {
   ... submit given tasks using executor ...
}

我有一个数据流,它应该被“包装”成可调用的并传递给这个函数。像下面这样的朴素映射不起作用:

Stream<String> ids = Stream.of("1", "2", "3");

invokeAll(ids.map((id) -> {
    // Do a long computation with given ID...
    return Boolean.TRUE; // Compilation error: Type mismatch: cannot convert from Boolean to Callable<Object>
}));

一种解决方案是返回返回 lambda 的 lambda:

invokeAll(ids.map((id) -> {
    return () -> {
        // Do a long computation with given ID...
        return Boolean.TRUE;
    };
}));

另一个(在某种程度上等效)是使用辅助函数:

public static <T> Callable<T> createCallable(T id) {
    return () -> {
        return id;
    };
}

invokeAll(ids.map(ThisClass::createCallable));

但也许有更好/更短的方法来做同样的事情?例如。以某种方式告诉编译器它需要创建一个返回给定值的Callable

invokeAll(ids.map((Function<String, Callable<Boolean>>) (id) -> {
    // Do a long computation with given ID
    return Boolean.TRUE;
}));

感谢您的任何建议。

【问题讨论】:

  • 实际上,第一个 sn-p 所需的唯一区别是将 (id) -&gt; { 更改为 (id) -&gt; () -&gt; {。为什么你认为这 4 个字符需要一个辅助方法?
  • 我不明白您为什么要隐藏 Callable 部分。在 lambda 前面添加一个 () -&gt; 只是一个,所以没什么大不了的。
  • 感谢您的 cmets!这对我来说是一个发现,从句法上讲,表达式可以写得更短,例如invokeAll(ids.map((id) -&gt; () -&gt; Boolean.TRUE));.
  • 更短:invokeAll(ids.map(id -&gt; () -&gt; true));...

标签: java lambda casting java-8 type-inference


【解决方案1】:

让我们暂时忽略 lambda,因为我认为它们是造成混乱的根源。让我们使用good old匿名类:

invokeAll(
    ids.map(
        new Function<String, Callable<Boolean>>()
        {
            @Override
            public Callable<Boolean> apply(String str)
            {
                return new Callable<Boolean>()
                {
                    @Override
                    public Boolean call() throws Exception
                    {
                        return Boolean.TRUE;
                    }
                };
            }
        }
    )
);

您实际上要问的是“我怎样才能自动做到这一点:”

invokeAll(
    ids.map(
        new Function<String, Callable<Boolean>>()
        {
            @Override
            public Callable<Boolean> apply(String str)
            {
                return Boolean.TRUE;
            }
        }
    )
);

当然,你不能Boolean 不是 Callable&lt;Boolean&gt;。因此,解决方案仅限于您已经确定的内容:

1) 使用 lambda 创建Callable

() -> Boolean.TRUE 
() -> { return Boolean.TRUE; }

2) 创建一个为您执行此操作的方法。例如方法可能比选项 #1 更冗长,因此它不会为您带来任何好处。

抱歉,没有其他方法可以自动使这变得更好。

【讨论】:

  • 与答案无关,但我个人也会避免将Stream 作为参数传递。这不是正确的国际海事组织。如果您希望您的方法传达“我将采取任何顺序的东西”的情绪,您应该使用Iterable。您可以根据需要从方法中的Iterable 创建一个流。
  • 也许你是对的,但从另一个角度来看,“任何东西序列”也是一个流。 Stream 可以转换为可迭代的,反之亦然——这样它们就可以互换。
  • @dma_k 我认为传递Stream 的问题在于调用者可能已经确定了它是否是并行的。它甚至可能已经关闭。我会说这是您希望调用者拥有的控制权。此外,Streams 通常较慢,因此如果您决定以后需要通过使用传统的 for 循环来提高性能,您将对方法签名进行大量更改。相反,为了提高可读性,您可以很容易地在任何/所有方法中将 Iterable 转换为 Stream
  • @dma_k 流和迭代器一样,只能处理一次,然后就全部用完。可迭代对象可以迭代多次。所以 Streams 不能转换为 Iterables。至少,不安全,或者很好。
猜你喜欢
  • 2018-05-15
  • 2023-02-02
  • 2012-03-07
  • 1970-01-01
  • 2021-08-10
  • 1970-01-01
  • 2021-05-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多