Retrofit(五) Return Type解析

本文详细介绍了Retrofit如何解析JavaType,重点在于理解不同类型的JavaType,如基本类型、参数化类型、泛型数组类型、类型变量和通配符表达式。Retrofit在创建ServiceMethod时,会对接口方法的返回类型进行限制,不允许存在泛型。在`validateServiceInterface`和`parseAnnotations`方法中,通过检查`getGenericReturnType`和`hasUnresolvableType`来确保返回类型符合规范。Retrofit的Utils工具类在处理类型检查方面发挥了关键作用。

在看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类,是非常好的工具类,以后用到相似的功能可以拿来用。

 

<think>好的,用户想了解如何在Retrofit中实现自定义解析器。首先,我需要回忆一下Retrofit的工作原理。Retrofit通过转换器(Converter)来处理请求和响应的数据格式,比如Gson或Moshi。当用户需要自定义解析器时,可能需要自己实现Converter.Factory。 根据提供的引用,特别是引用[4]提到可以添加Json转换器来自动解析响应。这说明自定义解析器需要继承Converter.Factory类,并重写相关方法。比如,responseBodyConverter方法用于处理响应体的转换,而requestBodyConverter处理请求体的转换。 接下来,用户可能需要一个具体的步骤说明。首先,创建自定义的Converter类,实现Converter接口,将响应体转换为所需类型。然后,创建对应的Factory类,注册到Retrofit实例中。这样Retrofit在处理特定类型时会使用自定义的转换器。 另外,用户可能需要示例代码。比如,处理纯文本响应或XML数据的情况。需要确保示例中的代码正确,比如使用@Body注解和正确设置Content-Type头。 还要注意,用户可能对如何注册自定义转换器有疑问,所以需要说明在构建Retrofit时使用addConverterFactory方法,并注意添加顺序,因为Retrofit会按顺序尝试转换器。 最后,检查是否有相关的问题,比如性能优化或错误处理,但根据用户的问题,重点应放在实现步骤和示例上。需要确保回答结构清晰,分步骤说明,并引用提供的参考资料中的信息,特别是引用[4]提到的转换器添加方式。</think>在Retrofit中实现自定义解析器,需要自定义`Converter.Factory`来处理特定数据格式。以下是分步实现方式: ### 一、自定义解析器实现步骤 1. **创建Converter实现类** ```java public class TextConverter implements Converter<ResponseBody, String> { @Override public String convert(ResponseBody value) throws IOException { return value.string().trim(); // 示例:去除响应首尾空格 } } ``` 2. **创建Converter.Factory子类** ```java public class TextConverterFactory extends Converter.Factory { public static TextConverterFactory create() { return new TextConverterFactory(); } @Override public Converter<ResponseBody, ?> responseBodyConverter( Type type, Annotation[] annotations, Retrofit retrofit ) { if (type == String.class) { return new TextConverter(); } return null; // 非String类型不处理 } } ``` ### 二、XML解析示例(扩展) ```java // XML转换器示例 public class XmlConverterFactory extends Converter.Factory { private final XmlMapper xmlMapper = new XmlMapper(); @Override public Converter<ResponseBody, ?> responseBodyConverter( Type type, Annotation[] annotations, Retrofit retrofit ) { return value -> xmlMapper.readValue(value.charStream(), (Class<?>) type); } @Override public Converter<?, RequestBody> requestBodyConverter( Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit ) { return value -> RequestBody.create( MediaType.get("application/xml"), xmlMapper.writeValueAsString(value) ); } } ``` ### 三、注册自定义解析器 ```kotlin val retrofit = Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(TextConverterFactory.create()) // 自定义解析器优先 .addConverterFactory(GsonConverterFactory.create()) // 默认解析器 .build() ``` ### 四、接口定义使用 ```java public interface ApiService { @GET("data") Call<String> getTextData(); // 使用TextConverter @POST("xml") @Headers("Content-Type: application/xml") Call<ResponseBody> postXmlData(@Body User user); // 使用XmlConverter } ``` **关键点说明:** 1. 转换器工厂的优先级由添加顺序决定,匹配到第一个适用的转换器即停止[^4] 2. 需同时实现`responseBodyConverter`和`requestBodyConverter`才能处理双向数据转换 3. 使用`@Body`注解时会触发请求体转换,`@Headers`需与转换器处理的媒体类型匹配[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值