parceler
parceler copied to clipboard
Parceler: Types do not match for property java.util.List<? extends
I am trying to convert the following working JUnit test from Java ...
import android.os.Parcelable;
import org.junit.Test;
import org.parceler.Parcel;
import org.parceler.ParcelPropertyConverter;
import org.parceler.Parcels;
import java.util.List;
import com.example.utils.LowEmissionZoneBuilder;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
public class ChildZonesParcelConverterTest {
@Test
public void testChildZonesParcelConverter() {
LowEmissionZone lowEmissionZone = new LowEmissionZoneBuilder().setName("LowEmissionZone1").build();
ParentZone parentZone = new ParentZone("ParentZone1", singletonList(lowEmissionZone));
Parcelable parcelable = Parcels.wrap(parentZone);
assertThat((ParentZone) Parcels.unwrap(parcelable)).isEqualTo(parentZone);
}
@Parcel
static class ParentZone {
public String name;
@ParcelPropertyConverter(ChildZonesParcelConverter.class)
public List<ChildZone> childZones;
public ParentZone() {
// For Parceler
}
public ParentZone(String name, List<ChildZone> childZones) {
this.name = name;
this.childZones = childZones;
}
}
}
... to Kotlin.
import com.example.utils.LowEmissionZoneBuilder
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.parceler.Parcel
import org.parceler.ParcelConstructor
import org.parceler.ParcelPropertyConverter
import org.parceler.Parcels
class ChildZonesParcelConverterTest {
@Test
fun testChildZonesParcelConverter() {
val lowEmissionZone = LowEmissionZoneBuilder().setName("LowEmissionZone1").build()
val parentZone = ParentZone("ParentZone1", listOf<ChildZone>(lowEmissionZone))
val parcelable = Parcels.wrap(parentZone)
assertThat(Parcels.unwrap<ParentZone>(parcelable)).isEqualTo(parentZone)
}
@Parcel(Parcel.Serialization.BEAN)
internal data class ParentZone @ParcelConstructor constructor(
val name: String,
@ParcelPropertyConverter(ChildZonesParcelConverter::class)
val childZones: List<ChildZone>
)
}
The relationship between ChildZone and LowEmissionZone is as follows:
@Parcel
public class LowEmissionZone implements ChildZone {}
Executing the Kotlin test fails with the following error:
e: /build/tmp/kapt3/stubs/debugUnitTest/com.example/models/ChildZonesParcelConverterTest.java:38: error:
Parceler: Types do not match for property childZones
java.util.List<? extends com.example.models.ChildZone>
java.util.List<com.example.models.ChildZone>
java.util.List<? extends com.example.models.ChildZone> childZones) {
^
e: /build/tmp/kapt3/stubs/debugUnitTest/com.example/models/ChildZonesParcelConverterTest.java:30: error:
Parceler: Types do not match for property childZones
java.util.List<? extends com.example.models.ChildZone>
java.util.List<com.example.models.ChildZone>
public final java.util.List<com.example.models.ChildZone> getChildZones() {
Related
Hmmm, are you able to show the Java classes Kotlin is generating for ParentZone @johnjohndoe? Looks like the Java constructor is accepting List<? extends ChildZone> while the getter is providing List<ChildZone>... which is appropriate. We might be able to accommodate this by relaxing some of the matching constraints in Parceler between the mutators and accessors.
Here is what I can find in build/tmp/kapt3/stubs/debugUnitTest/com/example/models/ChildZonesParcelConverterTest.java:
import java.lang.System;
@kotlin.Metadata(...)
public final class ChildZonesParcelConverterTest {
@org.junit.Test()
public final void testChildZonesParcelConverter() {
}
public ChildZonesParcelConverterTest() {
super();
}
@kotlin.Metadata(...)
@org.parceler.Parcel(value = org.parceler.Parcel.Serialization.BEAN)
public static final class ParentZone {
@org.jetbrains.annotations.NotNull()
private final java.lang.String name = null;
@org.jetbrains.annotations.NotNull()
private java.util.List<? extends com.example.models.ChildZone> childZones;
@org.jetbrains.annotations.NotNull()
public final java.lang.String getName() {
return null;
}
@org.jetbrains.annotations.NotNull()
public final java.util.List<com.example.models.ChildZone> getChildZones() {
return null;
}
public final void setChildZones(@org.jetbrains.annotations.NotNull()
java.util.List<? extends com.example.models.ChildZone> p0) {
}
@org.parceler.ParcelConstructor()
public ParentZone(@org.jetbrains.annotations.NotNull() java.lang.String name,
@org.jetbrains.annotations.NotNull()
@org.parceler.ParcelPropertyConverter(value = com.example.models.ChildZonesParcelConverter.class)
java.util.List<? extends com.example.models.ChildZone> childZones) {
super();
}
@org.jetbrains.annotations.NotNull()
public final java.lang.String component1() {
return null;
}
@org.jetbrains.annotations.NotNull()
public final java.util.List<com.example.models.ChildZone> component2() {
return null;
}
@org.jetbrains.annotations.NotNull()
public final com.example.models.ChildZonesParcelConverterTest.ParentZone copy(
@org.jetbrains.annotations.NotNull()
java.lang.String name,
@org.jetbrains.annotations.NotNull()
@org.parceler.ParcelPropertyConverter(value = com.example.models.ChildZonesParcelConverter.class)
java.util.List<? extends com.example.models.ChildZone> childZones) {
return null;
}
@org.jetbrains.annotations.NotNull()
@java.lang.Override()
public java.lang.String toString() {
return null;
}
@java.lang.Override()
public int hashCode() {
return 0;
}
@java.lang.Override()
public boolean equals(@org.jetbrains.annotations.Nullable()
java.lang.Object p0) {
return false;
}
}
}
Ah, that's perfect @johnjohndoe, thanks! I need to think on this a bit...
Here's a failing unit test to work off of, if you want to get your hands dirty @johnjohndoe:
Add this to ParcelableAnalysisTest:
@Parcel(Serialization.BEAN)
static class GenericMixedExtends {
private List<String> values;
public List<String> getValues() {
return values;
}
public void setValues(List<? extends String> values) {
this.values = new ArrayList<String>();
this.values.addAll(values);
}
}
@Test
public void testMixedGenericExtends() {
ParcelableDescriptor analysis = analyze(GenericMixedExtends.class);
assertFalse(messager.getMessage(), messager.isErrored()); // Fails here
assertNull(analysis.getParcelConverterType());
assertNotNull(analysis.getConstructorPair());
assertEquals(0, analysis.getFieldPairs().size());
assertEquals(1, analysis.getMethodPairs().size());
assertEquals(0, analysis.getConstructorPair().getWriteReferences().size());
assertEquals(0, analysis.getWrapCallbacks().size());
assertEquals(0, analysis.getUnwrapCallbacks().size());
}
changingList<? extends String> to just List<String> succeeds.
The indicated line fails with: Parceler: Types do not match for property values java.util.List<? extends java.lang.String> java.util.List<java.lang.String>. Basically if we could do a better job here, perhaps not just doing an equality, but a mutator matches accessor sort of thing that respects generics better then this wouldn't be an issue anymore.
Thank you for pointing into the source code!
However, I stepped away from using Kotlin data classes at the moment. The reason is that this would require me to add the jackson-module-kotlin library to the dependencies of my project which itself depends on kotlin-reflect. The latter is known for increasing the size of the final APK. I am trying to avoid this.
Therefore, I will not work on the topic in the near future.
Fair enough. I do appreciate this issue @johnjohndoe. Let me know if you need anything else.