Skip to content

Commit 558e410

Browse files
authored
Merge pull request #1138 from jqno/fix-kotlin-generics-issue
Handles generics (and bounded generics) in Kotlin lazy delegates
2 parents 3e90d93 + 660a901 commit 558e410

File tree

3 files changed

+59
-10
lines changed

3 files changed

+59
-10
lines changed

equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/kotlin/KotlinProbe.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ public static Optional<TypeTag> determineLazyType(Class<?> container, Field fiel
6767
}
6868

6969
private static TypeTag createTypeTag(KType kType) {
70-
KClass<?> kclass = (KClass<?>) kType.getClassifier();
71-
Class<?> rawClass = JvmClassMappingKt.getJavaClass(kclass);
70+
Class<?> rawClass = determineClass(kType);
7271

7372
if (kType.getArguments().isEmpty()) {
7473
return new TypeTag(rawClass);
@@ -85,6 +84,28 @@ private static TypeTag createTypeTag(KType kType) {
8584
return new TypeTag(rawClass, genericTypeTags);
8685
}
8786

87+
private static Class<?> determineClass(KType kType) {
88+
// The logic in this method follows that of TypeTag's inner methods
89+
var classifier = kType.getClassifier();
90+
91+
if (classifier instanceof KClass<?>) {
92+
var kclass = (KClass<?>) classifier;
93+
return JvmClassMappingKt.getJavaClass(kclass);
94+
}
95+
96+
if (classifier instanceof KTypeParameter) {
97+
var kTypeParameter = (KTypeParameter) classifier;
98+
for (KType b : kTypeParameter.getUpperBounds()) {
99+
var upper = determineClass(b);
100+
if (!Object.class.equals(upper)) {
101+
return upper;
102+
}
103+
}
104+
}
105+
106+
return Object.class;
107+
}
108+
88109
private static void assertHasKotlinReflect(Field f) {
89110
if (!KotlinScreen.canProbe()) {
90111
var msg = "Cannot read Kotlin field " + f.getName() + ". " + KotlinScreen.ERROR_MESSAGE;

equalsverifier-test-kotlin/src/test/kotlin/nl/jqno/equalsverifier/kotlin/KotlinDelegationTest.kt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,23 @@ class KotlinDelegationTest {
9696
}
9797

9898
@Test
99-
fun `succeed when class uses lazy generic delegate`() {
100-
EqualsVerifier.forClass(LazyGenericDelegation::class.java).verify()
99+
fun `succeed when class uses lazy delegate with generic value`() {
100+
EqualsVerifier.forClass(LazyDelegationWithGenericValue::class.java).verify()
101101
}
102102

103103
@Test
104-
fun `succeed when class uses nested lazy generic delegate`() {
105-
EqualsVerifier.forClass(NestedLazyGenericDelegation::class.java).verify()
104+
fun `succeed when class uses lazy delegate with nested lazy value`() {
105+
EqualsVerifier.forClass(LazyDelegationWithNestedGenericValue::class.java).verify()
106+
}
107+
108+
@Test
109+
fun `succeed when class is generic and uses that generic in its value`() {
110+
EqualsVerifier.forClass(LazyDelegationWithGenericClass::class.java).verify()
111+
}
112+
113+
@Test
114+
fun `succeed when class is generic with bound and uses that generic in its value`() {
115+
EqualsVerifier.forClass(LazyDelegationWithBoundedGenericClass::class.java).verify()
106116
}
107117

108118
@Test

equalsverifier-test-kotlin/src/test/kotlin/nl/jqno/equalsverifier/kotlin/delegates/KotlinDelegates.kt

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,20 +96,38 @@ class LazyDelegation(fooValue: Int) {
9696
override fun hashCode(): Int = foo
9797
}
9898

99-
class LazyGenericDelegation(fooValue: List<Int>) {
99+
class LazyDelegationWithGenericValue(fooValue: List<Int>) {
100100
val foo: List<Int> by lazy { fooValue }
101101

102102
override fun equals(other: Any?): Boolean =
103-
(other is LazyGenericDelegation) && foo == other.foo
103+
(other is LazyDelegationWithGenericValue) && foo == other.foo
104104

105105
override fun hashCode(): Int = foo.hashCode()
106106
}
107107

108-
class NestedLazyGenericDelegation(fooValue: List<List<List<Int>>>) {
108+
class LazyDelegationWithNestedGenericValue(fooValue: List<List<List<Int>>>) {
109109
val foo: List<List<List<Int>>> by lazy { fooValue }
110110

111111
override fun equals(other: Any?): Boolean =
112-
(other is NestedLazyGenericDelegation) && foo == other.foo
112+
(other is LazyDelegationWithNestedGenericValue) && foo == other.foo
113+
114+
override fun hashCode(): Int = foo.hashCode()
115+
}
116+
117+
class LazyDelegationWithGenericClass<T>(fooValue: T) {
118+
val foo: T by lazy { fooValue }
119+
120+
override fun equals(other: Any?): Boolean =
121+
(other is LazyDelegationWithGenericClass<*>) && foo == other.foo
122+
123+
override fun hashCode(): Int = foo.hashCode()
124+
}
125+
126+
class LazyDelegationWithBoundedGenericClass<T : Comparable<T>>(fooValue: T) {
127+
val foo: T by lazy { fooValue }
128+
129+
override fun equals(other: Any?): Boolean =
130+
(other is LazyDelegationWithBoundedGenericClass<*>) && foo == other.foo
113131

114132
override fun hashCode(): Int = foo.hashCode()
115133
}

0 commit comments

Comments
 (0)