File tree Expand file tree Collapse file tree 6 files changed +100
-13
lines changed
equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal
equalsverifier-test-kotlin/src/test/kotlin/nl/jqno/equalsverifier Expand file tree Collapse file tree 6 files changed +100
-13
lines changed Original file line number Diff line number Diff line change @@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
14
14
15
15
## [ Unreleased]
16
16
17
+ ### Added
18
+
19
+ - Support for Kotlin delegates. ([ Issue 1083] ( https://github.com/jqno/equalsverifier/issues/1083 ) )
20
+
17
21
## [ 4.0.1] - 2025-06-10
18
22
19
23
### Fixed
Original file line number Diff line number Diff line change @@ -131,18 +131,22 @@ private List<FieldProbe> addFieldsFor(Class<?> c) {
131
131
var statics = new ArrayList <FieldProbe >();
132
132
133
133
for (Field field : c .getDeclaredFields ()) {
134
- if (!field .isSynthetic ()
135
- && !"__cobertura_counters" .equals (field .getName ())
136
- && !field .getName ().startsWith ("bitmap$init$" ) // Generated by Scala 2.x's -Xcheckinit flag
137
- ) {
138
- FieldProbe probe = FieldProbe .of (field );
139
- boolean isStatic = probe .isStatic ();
140
- if (isStatic && includeStatic ) {
141
- statics .add (probe );
142
- }
143
- if (!isStatic ) {
144
- fields .add (probe );
145
- }
134
+ FieldProbe probe = FieldProbe .of (field );
135
+
136
+ if (field .isSynthetic () && !probe .isKotlinDelegate ()) {
137
+ continue ;
138
+ }
139
+ if (probe .getName ().startsWith ("bitmap$init$" ) // Generated by Scala 2.x's -Xcheckinit flag
140
+ || probe .getName ().equals ("__cobertura_counters" )) {
141
+ continue ;
142
+ }
143
+
144
+ boolean isStatic = probe .isStatic ();
145
+ if (isStatic && includeStatic ) {
146
+ statics .add (probe );
147
+ }
148
+ if (!isStatic ) {
149
+ fields .add (probe );
146
150
}
147
151
}
148
152
Original file line number Diff line number Diff line change @@ -170,12 +170,21 @@ public boolean isAnnotatedNonnull(AnnotationCache annotationCache) {
170
170
* @return Whether or not the field can be modified reflectively.
171
171
*/
172
172
public boolean canBeModifiedReflectively () {
173
- if (field .isSynthetic ()) {
173
+ if (field .isSynthetic () && ! isKotlinDelegate () ) {
174
174
return false ;
175
175
}
176
176
if (isFinal () && isStatic ()) {
177
177
return false ;
178
178
}
179
179
return true ;
180
180
}
181
+
182
+ /**
183
+ * Determines whether the field is a synthetic Kotlin delegate field.
184
+ *
185
+ * @return Whether or not the field is a synthetic Kotlin delegate field.
186
+ */
187
+ public boolean isKotlinDelegate () {
188
+ return field .isSynthetic () && field .getName ().startsWith ("$$delegate_" );
189
+ }
181
190
}
Original file line number Diff line number Diff line change 7
7
8
8
import nl .jqno .equalsverifier .Mode ;
9
9
import nl .jqno .equalsverifier .Warning ;
10
+ import nl .jqno .equalsverifier .internal .reflection .FieldIterable ;
11
+ import nl .jqno .equalsverifier .internal .reflection .FieldProbe ;
10
12
import nl .jqno .equalsverifier .internal .reflection .TypeTag ;
11
13
import nl .jqno .equalsverifier .internal .reflection .annotations .*;
12
14
@@ -97,6 +99,14 @@ public static <T> Configuration<T> build(
97
99
fieldnameToGetter != null ? fieldnameToGetter : Configuration ::defaulFieldNameToGetterConverter ;
98
100
boolean isKotlin = annotationCache .hasClassAnnotation (type , SupportedAnnotations .KOTLIN );
99
101
102
+ if (isKotlin ) {
103
+ for (FieldProbe f : FieldIterable .ofKotlin (type )) {
104
+ if (f .isKotlinDelegate ()) {
105
+ nonnullFields .add (f .getName ());
106
+ }
107
+ }
108
+ }
109
+
100
110
return new Configuration <>(type ,
101
111
typeTag ,
102
112
ignoredFields ,
Original file line number Diff line number Diff line change
1
+ package nl.jqno.equalsverifier.internal.reflection
2
+
3
+ import nl.jqno.equalsverifier.internal.reflection.FieldProbe
4
+ import org.junit.jupiter.api.Test
5
+
6
+ import org.assertj.core.api.Assertions.assertThat
7
+
8
+ class KotlinFieldProbeTest {
9
+
10
+ @Test
11
+ fun isKotlinDelegate () {
12
+ var f = FooContainer ::class .java.getDeclaredField(" \$\$ delegate_0" )
13
+ var probe = FieldProbe .of(f)
14
+ assertThat(probe.isKotlinDelegate()).isTrue()
15
+ }
16
+
17
+ @Test
18
+ fun isNotKotlinDelegate () {
19
+ var f = FooContainer ::class .java.getDeclaredField(" bar" )
20
+ var probe = FieldProbe .of(f)
21
+ assertThat(probe.isKotlinDelegate()).isFalse()
22
+ }
23
+
24
+ interface Foo {
25
+ val foo: Int
26
+ }
27
+
28
+ data class FooImpl (override val foo : Int ): Foo
29
+
30
+ class FooContainer (fooValue : Int , val bar : Int ): Foo by FooImpl(fooValue)
31
+ }
Original file line number Diff line number Diff line change
1
+ package nl.jqno.equalsverifier.kotlin
2
+
3
+ import nl.jqno.equalsverifier.EqualsVerifier
4
+ import org.junit.jupiter.api.Test
5
+
6
+ class KotlinDelegationTest {
7
+
8
+ @Test
9
+ fun `succeed when class uses interface delegation` () {
10
+ EqualsVerifier .forClass(FooContainer ::class .java).verify()
11
+ }
12
+
13
+ interface Foo {
14
+ val foo: Int
15
+ }
16
+
17
+ data class FooImpl (override val foo : Int ): Foo
18
+
19
+ class FooContainer (fooValue : Int ): Foo by FooImpl(fooValue) {
20
+
21
+ override fun equals (other : Any? ): Boolean {
22
+ if (this == = other) return true
23
+ if (other !is FooContainer ) return false
24
+ return foo == other.foo
25
+ }
26
+
27
+ override fun hashCode (): Int = foo
28
+ }
29
+ }
You can’t perform that action at this time.
0 commit comments