Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Class with Constraint access issue

Tags:

java

android

This a general JAVA question. In android, there is an interface Parcelable:

This is an example inside the official documentation:

 public class MyParcelable implements Parcelable {
     private int mData;

     public int describeContents() {
         return 0;
     }

     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mData);
     }

     public static final Parcelable.Creator<MyParcelable> CREATOR
             = new Parcelable.Creator<MyParcelable>() {
         public MyParcelable createFromParcel(Parcel in) {
             return new MyParcelable(in);
         }

         public MyParcelable[] newArray(int size) {
             return new MyParcelable[size];
         }
     };

     private MyParcelable(Parcel in) {
         mData = in.readInt();
     }
 }

We are required to implement the 2 functions describeContents() and writeToParcel.

(problem) Besides them, we are also required to implement the Parcelable.Creator<T> interface as a static field called CREATOR.

Everything is actually straight forward. Now I want to create a Generic Class that has a Parcelable Class Type:

public class ShushContainer<T extends Parcelable> implements Parcelable

I am able to implement Parcelable. I can call the T's writeToParcel function. However, I am unable to access the static CREATOR field.

public class ShushContainer<T extends Parcelable> implements Parcelable {
    Vector<T> items;
    String someField;

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(someField);
        dest.writeInt(items.size());
        for(int i=0;i<items.size();i++)
            items.get(i).writeToParcel(dest, flags);
    }

    public ShushContainer(Parcel in) {
        someField = in.readString();
        int size = in.readInt();
        for(int i=0;i<size;i++)
            //this does not work
            items.add(T.CREATOR.createFromParcel(in));
    }

    public static final Parcelable.Creator<ShushContainer> CREATOR = new Parcelable.Creator<ShushContainer>() {
        public ShushContainer createFromParcel(Parcel in) {
            return new ShushContainer(in);
        }

        public ShushContainer[] newArray(int size) {
            return new ShushContainer[size];
        }
    };
}
like image 700
Sherif elKhatib Avatar asked Sep 26 '12 12:09

Sherif elKhatib


People also ask

Can generic classes be constrained?

You can constrain the generic type by interface, thereby allowing only classes that implement that interface or classes that inherit from classes that implement the interface as the type parameter. The code below constrains a class to an interface.

Can a generic class have multiple constraints?

Multiple interface constraints can be specified. The constraining interface can also be generic. In a nullable context in C# 8.0 and later, T must be a non-nullable type that implements the specified interface.

How do you restrict a generic class?

Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class.

What are the constraints in generics?

The where clause in a generic definition specifies constraints on the types that are used as arguments for type parameters in a generic type, method, delegate, or local function. Constraints can specify interfaces, base classes, or require a generic type to be a reference, value, or unmanaged type.


1 Answers

Instead of <T extends Parcelable> you need <T extends ShushContainer<T> & Parcelable> this way you specify that T is ShushContainer so you can access is methods and variables.

public class ShushContainer<T extends ShushContainer<T> & Parcelable> 
implements Parcelable

Here is the example using Serializable

class Sample<T extends Sample<T> & Serializable> implements Serializable {

  public static int CONST = 0;

   public void foo()
   {
     T.CONST = 5;
   }
}

Update

If I understand correctly threre is another class which implements Parcelable which has CREATOR You are trying dynamic polymorphism for Static variables which is not possible.

Sample example to show how it fails

public class Base {
    public static int count = 10;
}

public class Child extends Base {
    public static int count = 20;
}


class Sample<T extends Base> {
    T t = null;
    public void printCount() {
        System.out.println(T.count);
    }
    public Sample(T t) {
        this.t = t;
    }

    public static void main(String[] args) {
        Sample<Child> sample = new Sample<Child>(new Child());
        Sample<Base> sample1 = new Sample<Base>(new Base());
        sample.printCount();//Child value printed as 10
        sample1.printCount();//Same for parent value printed as  10
    }

}

This program fails because static fields are bound to Class rather than instance so there are two separate count one for Base and one for Child if you access value of Base then it will always be 10.

You can use reflection to check whether CREATOR field is present and access it.Which will not be possible without object or class object.

Or You can do something like below using TypeToken

class Sample<T extends Serializable> implements Serializable {

    public int abc = 0;
    public void foo() {
    }
    public static void main(String[] args) throws SecurityException, NoSuchFieldException {
        TypeToken<Sample<Integer>> token = new TypeToken<Sample<Integer>>() {
        };
        Class<?> t = token.getRawType();
        Field field = t.getDeclaredField("abc");
        field.setAccessible(true);
        System.out.println(field.getName());

    }
}
like image 107
Amit Deshpande Avatar answered Sep 27 '22 20:09

Amit Deshpande