Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parceler: Types do not match for property java.util.List<? extends #366

Open
johnjohndoe opened this issue Feb 8, 2019 · 6 comments
Open

Comments

@johnjohndoe
Copy link
Contributor

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

@johncarl81
Copy link
Owner

johncarl81 commented Feb 8, 2019

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.

@johnjohndoe
Copy link
Contributor Author

johnjohndoe commented Feb 8, 2019

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;
        }
    }
}

@johncarl81
Copy link
Owner

Ah, that's perfect @johnjohndoe, thanks! I need to think on this a bit...

@johncarl81
Copy link
Owner

johncarl81 commented Feb 11, 2019

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.

@johnjohndoe
Copy link
Contributor Author

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.

@johncarl81
Copy link
Owner

Fair enough. I do appreciate this issue @johnjohndoe. Let me know if you need anything else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants