【问题标题】:Java8, bounded wildcard type not considered a functional interfaceJava8,有界通配符类型不被视为功能接口
【发布时间】:2018-04-20 10:07:16
【问题描述】:

在我的情况下,我有一个消费者接受? extends String 的供应商并对其执行一些操作,因此声明如下:

final Consumer<? super Supplier<? extends String>> action = ...

问题是当我尝试执行该操作时,编译器似乎并不高兴,并出现以下错误:

这个表达式的目标类型必须是函数式接口

在我的例子中,我有一个客户的名字,所以下面一行会触发这个错误:

action.accept(customer::getName)

我的假设是 ? super Supplier&lt;? extends String&gt; 不再被视为功能接口,因为它是一种捕获类型。

那么,有人可以给我一个关于这种情况的明确解释吗?

【问题讨论】:

  • 请告诉我们您使用的是哪个版本的 JDK,并提供完整的错误消息。
  • 不确定这是否是您的问题的真正原因,但仅仅因为Supplier@FunctionalInterfacenot 是否意味着它的任何超类/接口也是@FunctionalInterface。所以? super Supplier 可以删除为Object,这不是@FunctionalInterface。您可能不是指? super Supplier,而只是Supplier

标签: java java-8 functional-interface bounded-wildcard


【解决方案1】:

你为什么使用这个令牌,? super Supplier。这样做的目的是什么?你只需要传递一个Supplier,它被声明为@FunctionalInterface。试试这个吧,

public class MyConsumer implements Consumer<Supplier<String>> {
    @Override
    public void accept(Supplier<String> t) {
        System.out.println(t.get());

    }

    public static void main(String[] args) {
        MyConsumer myConsumer = new MyConsumer();
        myConsumer.accept(() -> "Test");
    }
}

@FunctionalInterface 注释对于代码的编译时间检查很有用。那里不能有多个方法。例如,RunnableCallable 是很好的候选对象。

但是你可以像这样进一步压缩它:

Consumer<Supplier<String>> myConsumer = (Supplier<String> supplier) -> System.out.println(supplier.get());
myConsumer.accept(() -> "Test");

由于编译器可以推断类型,这将进一步简化为,

Consumer<Supplier<String>> myConsumer = supplier -> System.out.println(supplier.get());
myConsumer.accept(() -> "Test");

【讨论】:

  • 还有,顺便说一句,? extends String 的目的是什么? (String 一直是 final 类。)
  • 有道理。我犯了一个愚蠢的错误,这是我的错。让我更新我的答案。感谢您指出这一点!
  • 是的,现在MyConsumer 的类型是Consumer&lt;Supplier&lt;? extends String&gt;&gt;,所以解决方案就是将action 转换为Consumer&lt;Supplier&lt;? extends String&gt;&gt;
  • 虽然你说的都是正确的,但我看不出它是如何回答问题的。
【解决方案2】:

这只是类型推断中的一个缺陷。请注意,虽然

final Consumer<? super Supplier<? extends String>> action=s -> System.out.println(s.get());
action.accept(() -> "foo");

因编译器错误而失败

final Consumer<? super Supplier<? extends String>> action=s -> System.out.println(s.get());
action.accept((Supplier<? extends String>)() -> "foo");

编译和运行没有问题。也一样

final Consumer<? super Supplier<? extends String>> action=s -> System.out.println(s.get());
final Supplier<? extends String> supp = () -> "foo";
action.accept(supp);

所以编译器根本没有为参数类型? super Supplier&lt;? extends String&gt;推断目标类型Supplier&lt;? extends String&gt;。请注意,另一方面,它会在提供 lambda 表达式时将 Supplier&lt;String&gt; 推断为分配给 Supplier&lt;? extends String&gt; 的目标类型。

【讨论】:

  • 所以也许这应该被报告为一个错误?
  • 我不认为这是一个“缺陷”:Consumer&lt;? super Supplier&gt; 甚至可以匹配Consumer&lt;Object&gt;,而Object 不是一个功能界面。通过向下转换为Supplier,您可以使潜在对象再次成为功能接口的实例。
  • @JimmyB Consumer&lt;? super Supplier…&gt; 可以Consumer&lt;Object&gt;,但您在action变量中看到编译器不假定Consumer&lt;Object&gt;为lambda表达式s -&gt; System.out.println(s.get()) 调用供应商的get 方法没有问题。在这里,编译器确实假定最具体的类型是Consumer&lt;Supplier&lt;? extends String&gt;&gt;。它可以对accept 的调用做同样的事情。请参阅我的答案的最后一个示例,它没有类型转换。这些分配是有效的。
  • @Holger 不,您的最后一个示例没有明确的演员表。但是您的supp 不是? super Supplier 类型,而是Supplier 类型,这与另一个具有显式强制转换的示例相同。当 using action 时,它的 declared 类型对编译器很重要,并且该类型不能保证涉及到供应商。换句话说,? super Supplier 意味着consumer.accept(new Object()) 将是有效的 w.r.t.泛型类型。因此,编译器只知道某些 object 是必需的,而无法看到customer::getName 应该如何针对非
  • @JimmyB 你可以consumer.accept(new Object())。声明Consumer&lt;? super Supplier&lt;? extends String&gt;&gt; action 意味着您可以将Consumer&lt;Object&gt; 分配给action,因为Consumer&lt;Object&gt; 还可以在其accept 方法中处理Supplier&lt;? extends String&gt; 参数。但由于也可以为其分配Consumer&lt;Supplier&lt;? extends String&gt;&gt;,正如在我的示例中隐式发生的那样,将new Object() 传递给accept 方法永远不会有效。 accept 的参数必须可以分配给Supplier&lt;? extends String&gt;。那么为什么不推断...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-30
  • 1970-01-01
  • 2018-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多