Retrofit(五) Return Type解析

在看CallAdapter的时候,发现需要根据接口方法的Return Type,来选择合适的CallAdapter。所以有必要了解一下 Java Type基础。

Java Type有以下几种:

  • 基本类型(Class):原始类型,每个类或接口都有个Class对象。
  • 参数化类型(ParameterizedType): 就是我们平常所用到的泛型List<String>、Map<K, V>, Set<T>,  Class<?>,ArrayList<? extends User>。
  • public interface ParameterizedType extends Type {
        // 获取<>中实际的类型参数,以Type数组形式返回
        Type[] getActualTypeArguments();
        // 获取<>前面的类型
        Type getRawType();
        // 如果这个类型是某个类型所属,则获取这个所有者的类型,否则返回null,比如Map.Entry<Sting,String>,会返回Map
        Type getOwnerType();
    }

     

  • 泛型数组类型(GenericArrayType):用来描述ParameterizedType、TypeVariable类型的数组。 并不是我们工作中所使用的数组String[] 、byte[],而是带有泛型的数组,即List<T>[], T[]。
public interface GenericArrayType extends Type {
    // 获得这个数组元素类型,比如T[]  则获得T的type
    Type getGenericComponentType();
}
  • 类型变量(TypeVariable):比如 T a。
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
    // 获取泛型的上限,无显示定义(extends),默认为Object
    Type[] getBounds();
    // 获取声明改类型变量实体(即获取类,方法或构造器名)
    D getGenericDeclaration();
    // 获取名称,即K、V、E之类名称
    String getName();
    
    AnnotatedType[] getAnnotatedBounds();
}
  • 泛型表达式或者通配符表达式(WildcardType),即? extends Number这样的表达式;WildcardType虽然是Type的子接口,但却不是Java类型中的一种。
public interface WildcardType extends Type {
    // 获取泛型表达式上界(上限extends)
    Type[] getUpperBounds();
    // 获取泛型表达式下界(下限super)
    Type[] getLowerBounds();
}
 

那么Retrofit是怎么解析Java Type的呢?

在Method方法中有一个getGenericReturnType()返回方法的Return Type。 这是Retrofit解析Return Type开端。

在ServiceMethod中对接口方法的返回类型做限制。

  • Raw type, 比如,T, T[], <? extends User>, 不被允许。不能有泛行。
  • ParamateredType,  Call<T>, Call<? extends User>, T[] , 这几种方式都是不被允许的。不能有泛行。
  • 总之就是不能有泛型。
  • 最后还有void类型。

在create()方法中有对接口类型的检查,是调用了validateServiceInterface(), 其中candidate.getTypeParameters().length != 0就是检查是不是有泛型参数。有的话就抛异常了。像public interface IFileTransferService<T>是不被允许的。

  private void validateServiceInterface(Class<?> service) {
    if (!service.isInterface()) {
      throw new IllegalArgumentException("API declarations must be interfaces.");
    }

    Deque<Class<?>> check = new ArrayDeque<>(1);
    check.add(service);
    while (!check.isEmpty()) {
      Class<?> candidate = check.removeFirst();
      if (candidate.getTypeParameters().length != 0) {
        StringBuilder message =
            new StringBuilder("Type parameters are unsupported on ").append(candidate.getName());
        if (candidate != service) {
          message.append(" which is an interface of ").append(service.getName());
        }
        throw new IllegalArgumentException(message.toString());
      }
    }
  }

 

在生成HttpServiceMethod的过程中,也有对类型的检查。是通过Utils.hasUnresolvableType()方法进行的。

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {

    Type returnType = method.getGenericReturnType();
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError(
          method,
          "Method return type must not include a type variable or wildcard: %s",
          returnType);
    }

    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }
  }
}

 

hasUnresolvableType()方法里面,已经限定了GenericArrayType, TypeVariable, WildcardType三种不能作为Return Type。

  static boolean hasUnresolvableType(@Nullable Type type) {
    if (type instanceof Class<?>) {
      return false;
    }
    if (type instanceof ParameterizedType) {
      ParameterizedType parameterizedType = (ParameterizedType) type;
      for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
        if (hasUnresolvableType(typeArgument)) {
          return true;
        }
      }
      return false;
    }
    if (type instanceof GenericArrayType) {
      return hasUnresolvableType(((GenericArrayType) type).getGenericComponentType());
    }
    if (type instanceof TypeVariable) {
      return true;
    }
    if (type instanceof WildcardType) {
      return true;
    }
    String className = type == null ? "null" : type.getClass().getName();
    throw new IllegalArgumentException(
        "Expected a Class, ParameterizedType, or "
            + "GenericArrayType, but <"
            + type
            + "> is of type "
            + className);
  }

 

Retrofit的Utils类,是非常好的工具类,以后用到相似的功能可以拿来用。

 


版权声明:本文为zjuter原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。