鉴于以下代码:
public static void main(String[] args) {
record Foo(int[] ints){}
var ints = new int[]{1, 2};
var foo = new Foo(ints);
System.out.println(foo); // Foo[ints=[I@6433a2]
System.out.println(new Foo(new int[]{1,2}).equals(new Foo(new int[]{1,2}))); // false
System.out.println(new Foo(ints).equals(new Foo(ints))); //true
System.out.println(foo.equals(foo)); // true
}
Run Code Online (Sandbox Code Playgroud)
显然,似乎使用了数组的toString,equals方法(而不是静态方法、Arrays::equals、Arrays::deepEquals 或Array::toString)。
所以我猜 Java 14 Records ( JEP 359 ) 不能很好地处理数组,必须使用 IDE 生成相应的方法(至少在 IntelliJ 中,默认情况下会生成“有用”的方法,即它们使用静态方法在Arrays)。
或者还有其他解决方案吗?
我想看看我是否可以用 Java 14 中的新 Record 类替换我现有的 Pojo。但无法这样做。得到以下错误:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException:无法构造实例
com.a.a.Post(无创建者,如默认构造,存在):无法从对象值反序列化(无基于委托或属性的创建者)
我知道错误是说记录没有构造函数,但是从我所看到的记录类在后台处理它,并且相关的 getter 也在后台设置(不完全是 getter,而是 id() title() 等等没有 get 前缀)。是不是因为 Spring 还没有采用最新的 Java 14 记录?请指教。谢谢。
我在 Spring Boot 版本 2.2.6 中执行此操作并使用 Java 14。
以下使用通常的 POJO 工作。
邮政类
public class PostClass {
private int userId;
private int id;
private String title;
private String body;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getId() {
return id;
}
public void setId(int id) { …Run Code Online (Sandbox Code Playgroud) Java 记录用于实现浅不可变的数据载体类型。如果构造函数接受可变类型,那么我们应该实现显式防御性复制以强制不变性。例如
record Data(Set<String> set) {
public Data(Set<Thing> set) {
this.set = Set.copyOf(set);
}
}
Run Code Online (Sandbox Code Playgroud)
这有点烦人 - 我们必须
理想情况下,我们想要表达的是以下内容:
record SomeRecord(ImmutableSet<Thing> set) {
}
Run Code Online (Sandbox Code Playgroud)
或者
record SomeRecord(Set<Thing> set) {
public SomeRecord {
if(set.isMutable()) throw new IllegalArgumentException(...);
}
}
Run Code Online (Sandbox Code Playgroud)
这里我们使用一个虚构的ImmutableSet类型和Set::isMutable方法,在任何一种情况下,记录都是使用规范构造函数创建的 - 很好。不幸的是它不存在!
据我所知,内置集合类型(在 Java 10 中引入)是隐藏的,即无法确定集合是否不可变(除了尝试修改它)。
我们可以使用 Guava,但是当 99% 的功能已经在核心库中时,这似乎有点过分了。或者,有 Maven 插件可以测试注释为不可变的类,但这又是一个创可贴而不是解决方案。
是否有任何纯 Java 机制来强制执行不可变集合?
我上过一堂课:
class A {
public final Integer orgId;
}
Run Code Online (Sandbox Code Playgroud)
我将其替换为Java 17中的记录:
record A (Integer orgId) {
}
Run Code Online (Sandbox Code Playgroud)
另外,我有一个通过反射进行验证的代码,该代码适用于常规类,但不适用于记录:
Field[] fields = obj.getClass().getFields(); //getting empty array here for the record
for (Field field : fields) {
}
Run Code Online (Sandbox Code Playgroud)
在 Java 17 中通过反射获取 Record 对象字段及其值的正确方法是什么?
有没有办法用 Java 记录做空对象?对于课程,我会这样做:
public class Id {
public static final Id NULL_ID = new Id();
private String id;
public Id(String id) {
this.id = Objects.requireNonNull(id);
}
private Id() {}
}
Run Code Online (Sandbox Code Playgroud)
但这不起作用,因为每个构造函数都需要经过规范的 ( Id(String id)构造函数,而我不能只是调用super()来绕过不变量。
public record Id(String id) {
public static final Id NULL_ID = null; // how?
public Id {
Objects.requireNonNull(id);
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
现在我解决了这个问题
public Id {
if (NULL_OBJECT != null)
Objects.requireNonNull(id);
}
Run Code Online (Sandbox Code Playgroud)
但这感觉不对,并且容易出现并发问题。
我还没有发现很多关于记录背后的设计思想的讨论,这可能已经讨论过了。如果像这样保持简单,那是可以理解的,但感觉很尴尬,我已经在小样本中多次遇到这个问题。
我在测试中有一段代码,使用 Hamcrest 2.2 检查结果列表是否包含某些属性:
assertThat(result.getUsers(), hasItem(
hasProperty("name", equalTo(user1.getName()))
));
assertThat(result.getUsers(), hasItem(
hasProperty("name", equalTo(user2.getName()))
));
Run Code Online (Sandbox Code Playgroud)
当NameDto是普通班级时,这工作得很好。但是在我将其更改为 a 之后Record,HamcresthasProperty抱怨没有名为 的属性name:
java.lang.AssertionError:
Expected: a collection containing hasProperty("name", "Test Name")
but: mismatches were: [No property "name", No property "name"]
Run Code Online (Sandbox Code Playgroud)
是否有其他匹配器可以用来实现与以前相同的匹配?或者我可以使用其他一些解决方法来让它处理记录?
想看看如何在 Java 16(和 15 个相同的行为)下使用带有反射的 Records
public record RecordTest2(int id, int something, double total, LocalDateTime createdOn) {
public RecordTest2(int id, int something, double total) {
this(id, something, total, LocalDateTime.now());
}
}
Run Code Online (Sandbox Code Playgroud)
查看规范构造函数,我确实看到了参数名称,但没有看到其他参数名称。
var recordTest2 = new RecordTest2(1, 2, 3.0, LocalDateTime.now());
Class<?> objectClass = recordTest2.getClass();
Constructor<?>[] constructors = objectClass.getConstructors();
for (Constructor<?> con : constructors) {
System.out.println(con.getName());
Parameter[] parameters = con.getParameters();
for (Parameter parameter : parameters) {
System.out.printf("param: %s\n", parameter.getName());
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
net.sf.persism.dao.records.RecordTest2
param: arg0
param: arg1
param: arg2
net.sf.persism.dao.records.RecordTest2
param: …Run Code Online (Sandbox Code Playgroud) 这令人惊讶:我能够用该名称声明一个变量,record即使它现在已成为关键字。看看这个:
public class Main {
static class Foo {
void bar() { System.out.println("Foo.bar"); }
}
record R (int a) {}
public static void main(String[] args) {
Foo record = new Foo();
record.bar();
R r = new R(5);
System.out.println(r);
}
}
Run Code Online (Sandbox Code Playgroud)
当使用 Java 17 编译并运行时,会给出:
Foo.bar
R[a=5]
Run Code Online (Sandbox Code Playgroud)
我原以为这会导致错误,就像尝试声明名为 的变量时的情况一样class。据我所知,Java 人员非常小心,不破坏现有代码,因此我认为这可能是一个经过深思熟虑的选择。
(你甚至不能声明一个名为 name 的变量,const因为它const 是Java 中的关键字。)
我一直在使用以下名为 City
@ToString
@AllArgsConstructor
public class City {
Integer id;
String name;
}
Run Code Online (Sandbox Code Playgroud)
并试图将其转换为一个record名为CityRecord作为
record CityRecord(Integer id, String name) {} // much cleaner!
Run Code Online (Sandbox Code Playgroud)
但是转向这样的表示,我们的单元测试之一开始失败。这些测试在内部处理从 JSON 文件中读取的城市列表,并映射到一个对象,进一步计算城市,同时将它们分组到Map. 简化为类似:
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()));
Run Code Online (Sandbox Code Playgroud)
上面的代码断言 true 包含 4 个键,每个键占其出现的 1 个。对于记录表示,结果中的键不超过 3 个Map。是什么导致了这种情况,应该有什么方法来解决这个问题?
我有一大组 POJO 类(100 多个),我想将它们转换为 Java 记录。我想自动化这个过程。
我使用的是 Java 18 (Amazon Coretto JDK) 和 IntelliJ 2022.1.4(终极版):Build #IU-221.6008.13, built on July 18, 2022
coretto-18被配置为模块的默认VM。
我遵循了 IntelliJ 文档(https://www.jetbrains.com/idea/guide/tips/convert-to-record/),但上下文操作不会显示选项“转换为记录”(预期) 。
java-record ×10
java ×9
java-14 ×4
java-16 ×3
java-17 ×2
arrays ×1
constructor ×1
hamcrest ×1
immutability ×1
jls ×1
reflection ×1
spring ×1
spring-boot ×1
unit-testing ×1