将类转换为记录时的兼容性问题 [英] Compatibility issues while converting Classes to Records
问题描述
我一直在与以下名为 City
的类一起工作
I have been working with the following class named City
@ToString
@AllArgsConstructor
public class City {
Integer id;
String name;
}
并尝试将其转换为记录
称为 CityRecord
as
and tried to convert it to a record
called CityRecord
as
record CityRecord(Integer id, String name) {} // much cleaner!
但是转向这样的表示形式,我们的单元测试之一开始失败。这些测试在内部处理从JSON文件读取并映射到对象的城市列表,从而进一步对城市进行计数,同时将其分组为 Map
。简化为以下内容:
But moving to such a representation, one of our unit tests starts failing. The tests internally deal with a list of cities read from a JSON file and mapped to an object further counting the cities while grouping them under into a Map
. Simplified to something like:
List<City> cities = List.of(
new City(1, "one"),
new City(2, "two"),
new City(3, "three"),
new City(2, "two"));
Map<City, Long> cityListMap = cities.stream()
.collect(Collectors.groupingBy(Function.identity(),
Collectors.counting()));
上面的代码断言true包含4个键,每个键占其出现的1个。使用记录表示法,在生成的 Map
中最多可以有3个键。是什么原因造成的,应该怎么解决?
The above code asserted true to contain 4 keys and each accounting for 1 of its occurrence. With the record representation, there are no more than 3 keys in the resulting Map
. What is causing this and what should be the way to go around this?
推荐答案
原因
观察到的行为背后的原因如 java.lang.Record
对于所有记录类,必须保持以下不变式:如果记录
R的成分是c1,c2,... cn,则如果按如下方式复制记录实例
:
For all record classes, the following invariant must hold: if a record R's components are c1, c2, ... cn, then if a record instance is copied as follows:
R copy = new R(r.c1(), r.c2(), ..., r.cn()); then it must be the case that r.equals(copy).
简而言之,您的 CityRecord
类现在具有 equals
(和哈希码)实现,该实现将两个属性进行比较,并确保它们是否相等,包含这些成分的记录也相等。评估的结果是,将具有相同属性的两个记录对象分组在一起。
In short, your CityRecord
class now has an equals
(and hashcode) implementation that compares the two attributes and ensure if they are equal the record consisting of those components are also equal. As a result of this evaluation, the two record objects with the same attributes would be grouped together.
因此,推断/断言应该有三个这样的键的结果是正确的而其中一个,名称=两个
被计算两次。
The result, therefore, would be correct to infer/assert that there should be three such keys with the one having id=2, name="two"
counted twice.
对此的一种立即解决方案是在您的记录表示中创建一个自定义(有瑕疵的原因,稍后说明)等于
的实现。看起来像这样:
An immediate temporary solution to this would be to create a custom(flawed - reason explained later) equals
implementation within your record representation as well. This would look like:
record CityRecord(Integer id, String name) {
// WARNING, BROKEN CODE
// Does not adhere to contract of `Record::equals`
@Override
public boolean equals(Object o) {
return this == o;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
}
现在,比较将是使用现有对象时的两个对象之间的比较 City
类,您的测试就可以正常进行。但是,在使用任何此类补救措施之前,您必须注意以下警告。
Now that the comparison would be between two objects as in while using the existing City
class, your tests would just work fine. But you must note the caution below before using any such remedy.
作为 JEP-359 读取,记录更像是数据载体,并且在选择迁移现有的类时,您必须了解记录自动获取的标准成员。
As the JEP-359 reads, Records are more like "data carrier" and while choosing to migrate your existing classes, you must be aware of the standard members acquired by a record automatically.
计划迁移一个类必须了解当前实现的完整详细信息,例如在您按 City
分组时引用的示例中,没有理由让两个城市的<$ c $相同c> id 和 name
data 的列出方式有所不同。它们应该相等,应该是所有重复的两次之后的相同数据,因此应该是正确的计数。
Planning to migrate one must be aware of the complete details of the current implementation, such as in the example you quoted while you've grouped by City
, there should be no reason to have two cities with same id
and name
data to be listed differently. They should be equal, it should be the same data after all repeated twice and hence the correct counts.
在这种情况下,您现有的实现(如果表示一个数据模型)可以通过覆盖等于
实现,以纠正与记录
匹配的方式。还要考虑比较各个属性,因此上述立即采取的补救措施是矛盾的,应该避免。
In which case, your existing implementation if representing a data model could be rectified to match the record
in a way by overwriting the equals
implementation to account for comparing the individual attributes as well which is where the immediate remedy stated above is contradictory and should be avoided.
这篇关于将类转换为记录时的兼容性问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!