【问题标题】:Generic method reference type specifying before/after :: operator指定之前/之后 :: 运算符的通用方法引用类型
【发布时间】:2019-01-18 01:52:44
【问题描述】:

以下方法引用有什么区别,

BiPredicate<List<String>,String> contains1 = List<String>::contains;

BiPredicate<List<String>,String> contains2 = List::<String>contains;

BiPredicate<List<String>,String> contains3 = List<String>::<String>contains;

箱子有特殊名称吗?有没有类似用法的例子?

【问题讨论】:

  • 相关:stackoverflow.com/questions/31245127/…。似乎第一个语法为List 指定了类型参数,而第二个语法为contains 指定了类型参数(在这种情况下是不必要的,因为该方法不是通用的)
  • 当然还有BiPredicate&lt;List&lt;String&gt;,String&gt; contains1 = List&lt;String&gt;::&lt;String&gt;contains;
  • 作为相关的旁注和部分解释:为非泛型方法提供类型参数是合法的,例如list.&lt;Number&gt;contains("foo")。他们只是被忽略了。 (至于为什么 JLS 的作者选择允许它,我不知道。)
  • @Radiodef 我不知道它的确切原因,但我怀疑你是 Herbert Schildt
  • @snr 为什么向非泛型方法提供类型参数是合法的?你可以找到答案here.

标签: java generics java-8 method-reference


【解决方案1】:

首先,这被称为 type witness (in the official Oracle Tutorial) 或 TypeArguments(在 JLS Sec 15.12 中),您正在有效地帮助编译器完成此类工作结构体。

一个例子:

private static void test(Callable<Object> call) {

}

private static void test(Runnable run) {

}

static class Gen<T> {

}

并通过test(Gen::new); 调用它(这将失败,别管为什么),但关键是您添加了一个 type witness 来帮助编译器,这样就可以了

test(Gen<String>::new);

所以当你写List&lt;String&gt;时,你已经为目标类型添加了一个类型见证——List,即;在第二种情况下,您正在为方法 contains 添加一个 - 但它不是通用的,所以它被忽略了。

【讨论】:

  • “类型见证”一词从何而来?我在语言规范中找不到它。
  • @AndyTurner docs.oracle.com/javase/tutorial/java/generics/… 搜索“类型见证”,我不是说它在 JLS 中(可能是,不确定)
  • @Lino 官方java教程是这样称呼的,我会坚持下去的:)
  • JLS 中没有出现“见证”这个词(至少 9 个)。他们在那里被简单地称为TypeArguments
【解决方案2】:

在:

BiPredicate<List<String>, String> contains2 = List::<String>contains;

&lt;String&gt; 是非泛型 List.contains 方法的类型参数1

在:

BiPredicate<List<String>, String> contains1 = List<String>::contains;

&lt;String&gt;List 的类型参数。


1 - 在这种特殊情况下,根据JLS §15.12.2.1 忽略类型参数:

非泛型方法可能适用于调用 提供显式类型参数。在这种情况下,类型 参数将被忽略。

【讨论】:

  • 在这种情况下,type parametertype argument 术语有什么区别吗?如果是,这是什么?
  • 是的,有区别。 List&lt;E&gt; 中的 E 是类型参数,List&lt;String&gt; 中的 String 是类型参数。您可以将泛型类型调用视为类似于普通方法调用,但不是将参数传递给方法,而是将类型参数(在这种情况下为 String)传递给 List 本身 (source).
【解决方案3】:

Intellij 告诉我关于它们的信息:

BiPredicate<List<String>, String> contains1 = List<String>::contains;

可以推断显式类型参数

BiPredicate<List<String>, String> contains2 = List::<String>contains;

类型参数对于非泛型方法引用是多余的

如果您要将它们拆分为各自的 lambda 函数,我相信您会看到以下内容:

BiPredicate<List<String>, String> contains1 = (List<String> strings, String o) -> strings.contains(o);
BiPredicate<List<String>, String> contains2 = (strings, o) -> strings.<String>contains(o);

正如我们所知,(List&lt;String&gt; strings, String o) 可以替换为(strings, o),并且第二行中的&lt;String&gt; 是不需要的(因为String#contains 不是通用的),因此可以安全地假设两个方法引用是等效的。

【讨论】:

    猜你喜欢
    • 2014-06-14
    • 2022-01-08
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多