java Type及项目实践
一. 泛型基础
泛型接口
public interface ITest<T> {
}
泛型方法
public <T> T testMethod(T param) {
return param;
}
匿名类
ITest t = new Itest<String> {
}
泛型边界
// 下面定义的泛型为带边界的泛型
public interface ITestWithUpper<T extend List> {
}
泛型擦除
Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。
如在代码中定义List
public class Test {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1); //这样调用 add 方法只能存储整形,因为泛型类型的实例为 Integer
list.getClass().getMethod("add", Object.class).invoke(list, "asd");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
// 其实运行可以发现,上述代码“asd”是可以被添加到List中去的,说明List存储的是原始对象(Object)
其实掌握以上基本上能cover住日常开发需求了。
二. type相关需求介绍
- 客户端对网络请求进行了封装
- 网络真实返回数据格式形如Result,而业务希望获取的时Result里面的data
Result<T> {
T data;
String code;
String extra;
String msg;
}
- 返回数据使用Gson解
data数据可能格式有两种:
{"key1":"value1","key2":"value2"}
[{"key1":"value1_0","key2":"value2_0"},{"key1":"value1_1","key2":"value2_1"}]
假如RealResult
public RealResult {
@SerialName("key1")
String key1;
@SerialName("key2")
String key2
}
- 封装层需要支持以上两种数据的解析,并通过Gson一次完成数据解析
解决方案:
private Type getResponseType() {
if (mListResult) {
// 生成List<T> 中的 List<T>
Type listType = new ParameterizedTypeImpl(List.class, new Class[]{mResultClz});
// 根据List<T>生成完整的Result<List<T>>
return new ParameterizedTypeImpl(Result.class, new Type[]{listType});
} else {
return new ParameterizedTypeImpl(Result.class, new Class[]{mResultClz});
}
}
ParameterizedTypeImpl代码如下:
public class ParameterizedTypeImpl implements ParameterizedType {
private final Class mRaw;
private final Type[] mArgs;
public ParameterizedTypeImpl(Class raw, Type[] args) {
this.mRaw = raw;
this.mArgs = args != null ? args : new Type[0];
}
@Override
public Type[] getActualTypeArguments() {
return mArgs;
}
@Override
public Type getRawType() {
return mRaw;
}
@Override
public Type getOwnerType() {
return null;
}
}
gson解析
Gson.fromJson(String xxxx, getResponseType()) 即可获取到Result类型的数据,
再将Result里面的data返回给业务。
三. Type
/**
*Type 是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
*从JDK1.5开始使用。
*/
public interface Type {
/**
* Returns a string describing this type, including information
* about any type parameters.
*
* @implSpec The default implementation calls {@code toString}.
*
* @return a string describing this type
* @since 1.8
* @hide Pending tests
*/
default String getTypeName() {
return toString();
}
}
原始类型:一般意义上的java类,由class类实现
参数化类型:ParameterizedType接口的实现类
数组类型:GenericArrayType接口的实现类
类型变量:TypeVariable接口的实现类
基本类型:int,float等java基本类型,其实也是class
网上贴出了一个例子:
public class TestReflect {
public static void test(TestReflect p0,
List<TestReflect> p1,
Map<String,TestReflect> p2,
List<String>[] p3,
Map<String,TestReflect>[] p4,
List<? extends TestReflect> p5,
Map<? extends TestReflect,? super TestReflect> p6){
}
以下代码获取七个参数的type
Method[] methods=TestReflect.class.getMethods();
Type[] types=oneMethod.getGenericParameterTypes(); // 获取p0-p6 7个参数的type
Class(原始类型/raw types)
普通的java类(比如String,Integer,Method等等),
数组,
自定义类(比如我们自己定义的TestReflect类),
8种java基本类型(比如int,float等)
可能还有其他的类
Class type0=(Class)types[0];
System.out.println("type0:"+type0.getName());
// 输出结果为: type0:com.selftest.test.testapp3.java_type.Types.TestReflect
ParameterizedType
当需要描述的类是泛型类时,比如List,Map等,不论代码里写没写具体的泛型,java会选择ParameterizedType接口做为Type的实现。
真正的实现类是sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl。
ParameterizedType接口有getActualTypeArguments()方法,用于得到泛型的Type类型数组。
//第二个参数,List< TestReflect > p1
Type type1=types[1];
Type[] parameterizedType1=((ParameterizedType)type1).getActualTypeArguments();
Class parameterizedType1_0=(Class)parameterizedType1[0]; // 如果有多个泛型都可以获取,如p2
System.out.println(parameterizedType1_0.getName());
//输出结果为: parameterizedType1_0:com.selftest.test.testapp3.java_type.Types.TestReflect
上述可以获取List中泛型的Type, 针对上面网络请求封装需求时使用的就是实现ParameterizedType,将泛型传给GSON,从而能直接从GSON中直接parse出泛型对象。
GenericArrayType
当需要描述的类型是泛型类的数组时,比如比如List[],Map[],type会用GenericArrayType接口作为Type的实现。
真正的实现类是sun.reflect.generics.reflectiveObjects. GenericArrayTypeImpl。
GenericArrayType接口有getGenericComponentType()方法,得到数组的组件类型的Type对象。
//第四个参数,List<String>[] p3
Type type3=types[3];
Type genericArrayType3=((GenericArrayType)type3).getGenericComponentType();
ParameterizedType parameterizedType3=(ParameterizedType)genericArrayType3;
Type[] parameterizedType3Arr=parameterizedType3.getActualTypeArguments();
Class class3=(Class)parameterizedType3Arr[0];
System.out.println("class3:"+class3.getName());
输出: class3:java.lang.String
WildcardType
当需要描述的类型是泛型类,而且泛型类中的泛型被定义为(? extends xxx)或者(? super xxx)这种类型,比如List<? extends TestReflect>,这个类型首先将由ParameterizedType实现,当调用ParameterizedType的getActualTypeArguments()方法后得到的Type就由WildcardType实现。
真正的实现类是sun.reflect.generics.reflectiveObjects. WildcardTypeImpl。
WildcardType接口有getUpperBounds()方法,得到的是类型的上边界的Type数组,实际上就是类型的直接父类,也就是extends后面的类型。显然在当前java的设定中,这个数组只可能有一个元素,因为java现在只能extends一个类。如果实在没写extends,那他的直接父类就是Object。
WildcardType接口有getLowerBounds()方法,得到的是类型的下边界的Type数组,有super关键字时可能会用到,经测试不会得到类型的子类,而是只得到super关键字后面的类型,如果没写super关键字,则返回空数组。
//第六个参数,List<? extends TestReflect> p5
Type type5=types[5];
Type[] parameterizedType5=((ParameterizedType)type5).getActualTypeArguments();
Type[] parameterizedType5_0_upper=((WildcardType)parameterizedType5[0]).getUpperBounds();
Type[] parameterizedType5_0_lower=((WildcardType)parameterizedType5[0]).getLowerBounds();
System.out.println("upper=" + parameterizedType5_0_upper[0]);
System.out.println("lower=" + parameterizedType5_0_lower.length);
// 输出:
// upper=class com.selftest.test.testapp3.java_type.Types.TestReflect
// lower=0
TypeVariable
Type的最后一种实现形式是TypeVariable接口,这种实现形式是在泛型类中使用的。
比如我们定义一个泛型类TestReflect
真正的实现类是sun.reflect.generics.reflectiveObjects. TypeVariableImpl。
public class TypeTestClass<RESULT> {}
TypeVariable[] types = TypeTestClass.class.getTypeParameters();
System.out.println("type=" + types[0].getName());
//输出为:type=RESULT, 只能获取显示的泛型名,不能获取到真正的类型