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

Generic class support #46

Closed
fabioCollini opened this issue Oct 21, 2014 · 12 comments
Closed

Generic class support #46

fabioCollini opened this issue Oct 21, 2014 · 12 comments

Comments

@fabioCollini
Copy link

I am trying to use parceler with a generic class I defined, this is a simplified example:

@Parcel
public class ParcelableObject {
    ParcelableInner<Integer> parcelableInner;
}

@Parcel
public class ParcelableInner<T> {
    T i;
}

Running it I get an exception:

Exception in thread "pool-177-thread-2" org.parceler.ParcelerRuntimeException: Unable to find appropriate Parcel method to write java.lang.Object

So I create a Converter to manual define how to convert the inner class:

@Parcel
@ParcelClass(value = ParcelableInner.class, converter = ParcelableInnerConverter.class)
public class ParcelableObject {
    ParcelableInner<Integer> parcelableInner;
}

public class ParcelableInner<T> {
    T i;
}

But I get a different error:

Exception in thread "pool-185-thread-1" org.parceler.ParcelerRuntimeException: Unable to find appropriate Parcel method to write it.cosenonjaviste.ParcelableInner

This error seems to be related to generic definition, so I removed the generic:

@Parcel
@ParcelClass(value = ParcelableInner.class, converter = ParcelableInnerConverter.class)
public class ParcelableObject {
    ParcelableInner parcelableInner;
}

public class ParcelableInner {
    Object i;
}

And the error is the first one again:

Exception in thread "pool-194-thread-1" org.parceler.ParcelerRuntimeException: Unable to find appropriate Parcel method to write java.lang.Object

In my opinion the last example is valid, I am using a converter so the framework validation is not needed. What do you think? Is there a different solution to use generic classes?

@johncarl81
Copy link
Owner

Parceler actually doesn't support Object as it is not a type represented within the Parcel (yes, there is the writeValue(Object) method, but internally it does an instanceof to determine what type to write).

It seems like your last example should work. I wonder if the ParcelClass converter parameter is not being picked up. I'll look into this.

@johncarl81
Copy link
Owner

Found a couple of bugs with the approach, but I found one that worked. Try this:

@Parcel
public class ParcelableObject {

    @ParcelProperty("test")
    @ParcelPropertyConverter(ParcelableInnerIntegerConverter.class)
    ParcelableInner<Integer> parcelableInner;

    public static class ParcelableInner<T> {
        T i;
    }

    public static class ParcelableInnerIntegerConverter implements ParcelConverter<ParcelableInner<Integer>> {

        @Override
        public void toParcel(ParcelableInner<Integer> input, android.os.Parcel parcel) {

        }

        @Override
        public ParcelableInner<Integer> fromParcel(android.os.Parcel parcel) {
            return null;
        }
    }
}

Currently Parceler doesn't allow you to annotate a field with @ParcelPropertyConverter without an accompanying @ParcelProperty annotation. I will be pushing out a fix for this shortly.

@fabioCollini
Copy link
Author

Thanks for your answer, your example works but it's a bit verbose, two annotations every time you need to use the inner class.

Another working solution is this:

@Parcel
@ParcelClass(value = ParcelableObject.ParcelableInner.class, converter = ParcelableObject.ParcelableInnerIntegerConverter.class)
public class ParcelableObject {

    ParcelableInner parcelableInner;

    public static class ParcelableInner<T> {
        @Transient
        T i;
    }

    public static class ParcelableInnerIntegerConverter implements ParcelConverter<ParcelableInner<Integer>> {

        @Override
        public void toParcel(ParcelableInner<Integer> input, android.os.Parcel parcel) {

        }

        @Override
        public ParcelableInner<Integer> fromParcel(android.os.Parcel parcel) {
            return null;
        }
    }
}

But I don't like it because the field ParcelableInner is not defined as a generic (it should be ParcelableInner. Is this a bug that you already fixed?

@johncarl81
Copy link
Owner

Yes, @fabioCollini, I fixed the requirement to have a @ParcelProperty with a @ParcelPropertyConverter (909b4ab). This should work with 0.2.14-SNAPSHOT:

@Parcel
public class ParcelableObject {

    @ParcelPropertyConverter(ParcelableInnerIntegerConverter.class)
    ParcelableInner<Integer> parcelableInner;

    public static class ParcelableInner<T> {
        T i;
    }

    public static class ParcelableInnerIntegerConverter implements ParcelConverter<ParcelableInner<Integer>> {

        @Override
        public void toParcel(ParcelableInner<Integer> input, android.os.Parcel parcel) {

        }

        @Override
        public ParcelableInner<Integer> fromParcel(android.os.Parcel parcel) {
            return null;
        }
    }
}

Give it a try and please let me know if it works for you.

@fabioCollini
Copy link
Author

Now using ParcelPropertyConverter it works correctly but there is still an error using ParcelClass:

@Parcel
@ParcelClass(value = ParcelableObject.ParcelableInner.class, converter = ParcelableObject.ParcelableInnerIntegerConverter.class)
public class ParcelableObject {

    ParcelableInner<Integer> parcelableInner;

    public static class ParcelableInner<T> {
        T i;
    }

    public static class ParcelableInnerIntegerConverter implements ParcelConverter<ParcelableInner<Integer>> {

        @Override
        public void toParcel(ParcelableInner<Integer> input, android.os.Parcel parcel) {

        }

        @Override
        public ParcelableInner<Integer> fromParcel(android.os.Parcel parcel) {
            return null;
        }
    }
}

Exception in thread "pool-556-thread-1" org.parceler.ParcelerRuntimeException: Unable to find appropriate Parcel method to write it.cosenonjaviste.ParcelableObject.ParcelableInner

Could you fix this example too?
Thanks again!

@johncarl81
Copy link
Owner

That example is unfortunately not going to work with some limitations of annotations. Parceler does a deep equality between classes, thus:

ParcelableInner.class != ParcelableInner<Integer>.class

This would work if we could define the annotation like so:

@ParcelClass(value = ParcelableObject.ParcelableInner<Integer>.class, converter = ParcelableObject.ParcelableInnerIntegerConverter.class)

For instance, this should work (with the latest SNAPSHOT... I had to fix a bug related to this):

@Parcel
@ParcelClass(value = ParcelableObject.ParcelableInnerInteger.class, converter = ParcelableObject.ParcelableInnerIntegerConverter.class)
public class ParcelableObject {

    ParcelableInnerInteger parcelableInner;

    public static class ParcelableInnerInteger extends ParcelableInner<Integer>{}

    public static class ParcelableInner<T> {
        T i;
    }

    public static class ParcelableInnerIntegerConverter implements ParcelConverter<ParcelableInnerInteger> {

        @Override
        public void toParcel(ParcelableInnerInteger input, android.os.Parcel parcel) {

        }

        @Override
        public ParcelableInnerInteger fromParcel(android.os.Parcel parcel) {
            return null;
        }
    }
}

@fabioCollini
Copy link
Author

Are you sure? Executing Parcels.wrap(new ParcelableObject.ParcelableInner()) works, Parcels.REPOSITORY contains the right data. Why don't you just ignore generics when a field is defined using generic types?

@johncarl81
Copy link
Owner

@fabioCollini, can you clarify? How did you annotate your ParcelableInner class?

@fabioCollini
Copy link
Author

@johncarl81
Copy link
Owner

Using a Serializable object is not ideal, but if it's the only way to transfer your Throwable you may be out of luck. Could you annotate your Throwable with @Parcel?

What sort of guide are you looking for? I was thinking about putting together a proper website. Currently the github README is the best place for documentation.

@fabioCollini
Copy link
Author

You are right about the throwable, but I defined a converter for the base class. I am extending this class but it write the field of the base class in the parcel as a serializable (the converter on the base class is ignored).

@johncarl81
Copy link
Owner

Looks like everything is in order... you good to close or did we not address this?

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