在此问题中用户@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字符串以及每个Person
id:
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
这是可以预期的,因为field
在Object
类中没有命名成员。
所以我想知道:
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 内部类: 在一个类中定义另一个类,这样定义的类称为内部类。【包含内部类的类可以称为内部类的外部类】 如果想要通过一个类来使用另一个类,可以定