linkedin / dexmaker

A utility for doing compile or runtime code generation targeting Android's Dalvik VM
Apache License 2.0
1.86k stars 248 forks source link

How to generate a class with generics type? #163

Closed liangxiwei closed 4 years ago

liangxiwei commented 4 years ago

I have a class call A. and I want to generics a class which has generics type,like this: public class Hello\<A>{

}

how should I do? does the dexmaker support it?

kkoser commented 4 years ago

I haven't tested this, but I believe you would just generate a normal class, and sub A for whatevers its base type is. So for example if you were doing something like you would sub view. Type erasure means that these are all treated as the base type at runtime

liangxiwei commented 4 years ago

I haven't tested this, but I believe you would just generate a normal class, and sub A for whatevers its base type is. So for example if you were doing something like you would sub view. Type erasure means that these are all treated as the base type at runtime

some code like this:

public class HttpCallback\<T>{   void onSuccess(T t) {   } }

HttpUtil.get(url).callback(new HttpCallback\<UserInfo>(){   public void onSuccess(UserInfo info) {    //do something   } })

And the code use some reflect library like gson to auto generate UserInfo,It must be know generics type.

If I want to make a HttpCallBack subClass,It must has generics type.

kkoser commented 4 years ago

Type erasure still applies, you can read about it here: https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

If you have an object of a non-generic class, you can view it's fields etc. But if you have a raw generic type (without knowing for your use case specifically what the generic part is at compile time, so ex if you have a method that takes HttpCallBack and NOT HttpCallBack<String>), you cannot infer the type of the generic at runtime from the type of that class itself. Since dexmaker works entirely with runtime code generation, and the compiler cannot know anything about the types dexmaker is making ahead of time, all generics in dexmaker would be raw generics, which are just a normal class where the generic is defined by its upper bound (so in the example above, Object as no bound was defined).

So I would recommend that you define your classes using the bounds they will have at runtime, ex:

public class HttpCallback{
  void onSuccess(Object t) {
  }
}

within the onSuccess method (where you have an instance of your generic class available), you can inspect it to derive its typing information, so you would be able to for instance parse it using gson via the getClass method to provide typing information.

If you take a look at how gson deals with collections, you can see that they also run into this problem:

// Deserialization
Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
Collection<Integer> ints2 = gson.fromJson(json, collectionType);
// ==> ints2 is same as ints
Fairly hideous: note how we define the type of collection. Unfortunately, there is no way to get around this in Java.

(https://github.com/google/gson/blob/master/UserGuide.md) (https://www.javadoc.io/doc/com.google.code.gson/gson/2.6.2/com/google/gson/reflect/TypeToken.html)

They need the TypeToken class to be able to infer the types of the generic item inside a collection at runtime. If you truly need to know the generic types of your generated classes before you have an instance of the type, you would need a similar approach to that, so that the user can provide that typing information to you

liangxiwei commented 4 years ago

thanks a lot!