diff --git a/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/types/ExpressionBase.java b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/types/ExpressionBase.java index 7a92e11c55..27509daaef 100644 --- a/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/types/ExpressionBase.java +++ b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/types/ExpressionBase.java @@ -30,7 +30,7 @@ public abstract class ExpressionBase implements Expression { @Nullable private transient volatile String toString; - @Nullable private transient volatile Integer hashCode; + private transient volatile int hashCode; public ExpressionBase(Class type) { this.type = type; @@ -43,10 +43,12 @@ public final Class getType() { @Override public final int hashCode() { - if (hashCode == null) { - hashCode = accept(HashCodeVisitor.DEFAULT, null); + var hash = hashCode; + if (hash == 0) { + hash = accept(HashCodeVisitor.DEFAULT, null); + hashCode = hash; } - return hashCode; + return hash; } @Override diff --git a/querydsl-libraries/querydsl-core/src/test/java/com/querydsl/core/serialization/SerializerBaseTest.java b/querydsl-libraries/querydsl-core/src/test/java/com/querydsl/core/serialization/SerializerBaseTest.java index 1faea6e194..a80aca759d 100644 --- a/querydsl-libraries/querydsl-core/src/test/java/com/querydsl/core/serialization/SerializerBaseTest.java +++ b/querydsl-libraries/querydsl-core/src/test/java/com/querydsl/core/serialization/SerializerBaseTest.java @@ -13,6 +13,8 @@ */ package com.querydsl.core.serialization; +import static org.assertj.core.api.Assertions.assertThat; + import com.querydsl.core.types.ConstantImpl; import com.querydsl.core.types.ExpressionUtils; import com.querydsl.core.types.JavaTemplates; @@ -38,4 +40,38 @@ void test() { // custom serializer.handle(ExpressionUtils.template(Object.class, "xxx", ConstantImpl.create(""))); } + + /** + * Constants are labelled by identity, so two equal but distinct instances are bound as two + * separate parameters. Boxed values outside the {@link Long} cache are distinct instances today; + * once the JDK migrates the wrappers to value classes (JEP 401) {@code ==} becomes state based + * and this collapses to a single label. + */ + @Test + void equalButDistinctConstantsGetDistinctLabels() { + var serializer = new DummySerializer(new JavaTemplates()); + Long first = 1000L; + Long second = 1000L; + assertThat(first).isNotSameAs(second).isEqualTo(second); + + serializer.handle((Object) first); + serializer.handle((Object) second); + + assertThat(serializer.getConstants()).containsExactly(first, second); + assertThat(serializer.getConstantToLabel()).hasSize(2); + assertThat(serializer).hasToString("a1a2"); + } + + @Test + void repeatedConstantInstanceReusesLabel() { + var serializer = new DummySerializer(new JavaTemplates()); + Long value = 1000L; + + serializer.handle((Object) value); + serializer.handle((Object) value); + + assertThat(serializer.getConstants()).containsExactly(value, value); + assertThat(serializer.getConstantToLabel()).hasSize(1); + assertThat(serializer).hasToString("a1a1"); + } }