当前位置: 首页 > 面试题库 >

Lambda内的隐含匿名类型

卢作人
2023-03-14
问题内容

在此问题中用户@Holger提供了一个答案,该答案显示了匿名类的不常见用法,我并不知道。

该答案使用流,但是此问题与流无关,因为这种匿名类型构造可以在其他上下文中使用,即:

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方法链传播的。

现在,我可以想象到此功能非常有用的用法…很多时候,我需要对管道进行一些map操作,Stream同时还要保留原始元素,即假设我有一个人列表:

public class Person {
    Long id;
    String name, lastName;
    // getters, setters, hashCode, equals...
}

List<Person> people = ...;

我需要将Person实例的JSON表示形式存储在某个存储库中,为此,我需要每个Person实例的JSON字符串以及每个Personid:

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

这是可以预期的,因为fieldObject类中没有命名成员。

所以我想知道:

  • 是在JLS的某个地方记录了此事吗?
  • 这有什么限制(如果有)?
  • 这样编写代码实际上 安全 吗?
  • 是否有速记语法,或者这是我们能做到的最好的语法?

问题答案:

JLS中没有提到这种用法,但是,当然,该规范不能通过列举编程语言提供的所有可能性来起作用。取而代之的是,您必须应用关于类型的形式规则,并且它们对匿名类型也不例外,换句话说,规范在任何时候都没有说表达式的类型必须回退到命名的super类型中。匿名类的情况。

诚然,我可能会在规范的深处忽略这样的声明,但是对我来说,关于匿名类型的唯一限制总是源于它们的 匿名 性质,这自然是很自然的,也就是说,每种需要
通过name 引用类型 语言构造都可以不能直接使用该类型,因此您必须选择一个超类型。

因此,如果表达式new Object() { String field; }的类型是包含字段“ field” 的匿名类型,则不仅访问new Object() { String field; }.field将起作用,而且Collections.singletonList(new Object() { String field; }).get(0).field除非明确的规则禁止且一致地进行访问,否则该方法同样适用于lambda表达式。

从Java
10开始,您可以使用var声明其类型从初始化程序推断出的局部变量。这样,您现在可以声明具有匿名类类型的任意局部变量,而不仅仅是lambda参数。例如,以下作品

var obj = new Object() { int i = 42; String s = "blah"; };
obj.i += 10;
System.out.println(obj.s);

同样,我们可以使您的问题示例起作用:

var optional = Optional.of(new Object() { String field = s; });
optional.map(anonymous -> anonymous.field).ifPresent(System.out::println);

在这种情况下,我们可以参考显示类似示例的规范,该示例表明这不是疏忽,而是预期的行为:

var d = new Object() {};  // d has the type of the anonymous class

另一个提示变量通常具有不可表示类型的可能性:

var e = (CharSequence & Comparable<String>) "x";
                          // e has type CharSequence &

Comparable

也就是说,我必须警告不要过度使用该功能。除了对可读性的关注(您称自己为“不常见的用法”)之外,在每个使用它的地方,您都在创建一个不同的新类(与“双括号初始化”相对)。它不像其他编程语言的实际元组类型或未命名类型那样,它们会平等地对待同一组成员的所有出现。

同样,像这样创建的实例将new Object() { String field = s;}消耗所需内存的两倍,因为它不仅包含声明的字段,而且还包含用于初始化字段的捕获值。在new Object() { Long id =p.getId(); String json = toJson(p); }示例中,您需要为存储三个引用而不是两个引用付费p。在非静态上下文中,匿名内部类也始终捕获周围环境this



 类似资料:
  • 这个答案使用了流,但这个问题不是关于流的,因为这个匿名类型构造可以在其他上下文中使用,即: 令我惊讶的是,这编译并打印了预期的输出。 注意:我很清楚,自古以来,就有可能构造一个匿名的内部类,并按如下方式使用它的成员: 并且我需要在某个存储库中存储实例的JSON表示,为此,我需要每个实例以及每个ID的JSON字符串: 在本例中,我丢失了字段,因为我已经将每个人转换为其对应的json字符串。 为了避免

  • 编译工作正常,但在运行时应用程序崩溃,出现以下错误:。 和缺少了什么?

  • 问题内容: 在进行一些基本的lambda练习时,一个看似完全相同的匿名内部类的输出给我的输出与lambda不同。 场景1 输出 2 和 2 。这里没有新内容。 但是当我这样做时: 场景2 输出 2 和 3 问题:两个输出不应该相同吗? 我想念什么吗? 为了完整起见: 方案3 输出 3 和 3 。这里也没有什么新鲜的。 更新:仍从1.8.0-b132获得相同的输出 更新#2:错误报告: https

  • 下面的代码可以工作,但我收到了SonarLint的通知,因为我在流中使用了匿名类而不是lambda表达式,我不知道如何改进下面的代码来避免通知: 代码解释:我使用java.util的属性类,不幸的是,属性的返回

  • 以下代码收到Sonarint的通知: 我尝试了以下内容,但在抛出新的IllegalArgumentException时遇到了问题: 你能建议一下吗?

  • 本文向大家介绍Java匿名类,匿名内部类实例分析,包括了Java匿名类,匿名内部类实例分析的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了Java匿名类,匿名内部类。分享给大家供大家参考,具体如下: 内部类 匿名类  首发日期 :2018-03-25 内部类: 在一个类中定义另一个类,这样定义的类称为内部类。【包含内部类的类可以称为内部类的外部类】 如果想要通过一个类来使用另一个类,可以定