【发布时间】:2017-10-14 16:46:01
【问题描述】:
在this question 中,用户@Holger 提供了an answer,它显示了匿名类的不常见用法,我不知道。
该答案使用流,但此问题与流无关,因为这种匿名类型构造可以在其他上下文中使用,即:
String s = "Digging into Java's intricacies";
Optional.of(new Object() { String field = s; })
.map(anonymous -> anonymous.field) // anonymous implied type
.ifPresent(System.out::println);
令我惊讶的是,它编译并打印了预期的输出。
注意:我很清楚,自古以来,就可以构造一个匿名内部类并使用它的成员如下:
int result = new Object() { int incr(int i) {return i + 1; } }.incr(3);
System.out.println(result); // 4
但是,这不是我在这里要问的。我的情况不同,因为匿名类型是通过Optional方法链传播的。
现在,我可以想象这个功能的一个非常有用的用法......很多时候,我需要在Stream 管道上发出一些map 操作,同时还要保留原始元素,即假设我有一个人员名单:
public class Person {
Long id;
String name, lastName;
// getters, setters, hashCode, equals...
}
List<Person> people = ...;
而且我需要在某个存储库中存储我的Person 实例的 JSON 表示,为此我需要每个 Person 实例以及每个 Person id 的 JSON 字符串:
public static String toJson(Object obj) {
String json = ...; // serialize obj with some JSON lib
return json;
}
people.stream()
.map(person -> toJson(person))
.forEach(json -> repository.add(ID, json)); // where's the ID?
在此示例中,我丢失了 Person.id 字段,因为我已将每个人都转换为其对应的 json 字符串。
为了规避这个问题,我看到很多人使用某种Holder 类,或者Pair,甚至Tuple,或者只是AbstractMap.SimpleEntry:
people.stream()
.map(p -> new Pair<Long, String>(p.getId(), toJson(p)))
.forEach(pair -> repository.add(pair.getLeft(), pair.getRight()));
虽然这对于这个简单的示例来说已经足够了,但它仍然需要存在一个通用的 Pair 类。如果我们需要通过流传播 3 个值,我认为我们可以使用 Tuple3 类等。使用数组也是一种选择,但它不是类型安全的,除非所有值都是相同的类型。
因此,使用隐含的匿名类型,上面相同的代码可以重写如下:
people.stream()
.map(p -> new Object() { Long id = p.getId(); String json = toJson(p); })
.forEach(it -> repository.add(it.id, it.json));
太神奇了!现在我们可以拥有任意数量的字段,同时还保持类型安全。
在测试时,我无法在单独的代码行中使用隐含类型。如果我修改我的原始代码如下:
String s = "Digging into Java's intricacies";
Optional<Object> optional = Optional.of(new Object() { String field = s; });
optional.map(anonymous -> anonymous.field)
.ifPresent(System.out::println);
我得到一个编译错误:
Error: java: cannot find symbol
symbol: variable field
location: variable anonymous of type java.lang.Object
这是意料之中的,因为在 Object 类中没有名为 field 的成员。
所以我想知道:
- 这是否记录在某处或 JLS 中是否有相关内容?
- 这有什么限制(如果有的话)?
- 这样写代码真的安全吗?
- 是否有这方面的简写语法,或者这是我们能做的最好的吗?
【问题讨论】:
-
方法对应效果见here。在这里,泛型类型推断公开了匿名类型,您可以在其中发现字段。在上一个示例中,您专门使用了
java.lang.Object,它没有此类字段。 -
虽然这不会在 Eclipse 上编译:\
-
@SotiriosDelimanolis 这实际上很棒,但我不认为它们完全相同。当直接调用一个方法时,你有点像
chainthis - 类型就在那里,你只是无法表达它。在 lambda 创建中,类型将在各个阶段传递。我发现不同 -
据我所知,没有地方说这应该有效,实际上是相反的;没有地方说表达式的类型应该回退到基类,如果它是匿名的。因此,
new Object() { void foo() {} }.foo()起作用的原因与其他涉及 lambda 的示例起作用的原因相同。但正如所说,“据我所知”,一个反例指向 JLS 中的一个地方说“不不”可能证明我错了。 -
@SotiriosDelimanolis @Eugene 它在 Neon 中为我编译。它甚至显示
of的推断类型:<Main(){}> Optional<Main(){}> java.util.Optional.of(Main(){} value)
标签: java lambda java-8 language-lawyer anonymous-types