首页 > 解决方案 > 获取泛型类的属性值

问题描述

我正在使用 Java v1.8,并且我有以下代码:

public <T> ApiResponse<T> execute(Call call, Type returnType) throws ApiException {
    try {
        Response response = call.execute();
        T data = handleResponse(response, returnType);

        ApiResponse<T> apiResponse = new ApiResponse<T>(response.code(), response.headers().toMultimap(), data);

        return apiResponse;
    } catch (IOException e) {
        throw new ApiException(e);
    }
}

所以这会进行 API 调用并根据Type returnType. 我知道每个 API 调用都会响应 body 中的参数requestId

由于我不知道的类型returnType,有什么办法可以得到财产requestId。它应该在data...

标签: java

解决方案


您已经定义了一个类型变量T。这个类型变量没有任何界限;您将其声明为<T>,这意味着 T 可以是任何东西。甚至对象。特别Object是,任何 T 可能必须是其子类型的通用类型,并且 Object没有 requestId。因此,您不能调用.getRequestId()or.requestId或任何您对requestId. 解决方案是绑定T:告诉 java T 不能只是“任何东西”,它只能是具有某些属性的类型。具体来说,告诉它它只能是任何抽象类或接口,它是所有 Api 响应返回类型的超类型,.getRequestId()里面有一个方法:public <T extends ThatSharedSuperType> ....是你想要的。

这里还有第二个问题——你的泛型刚刚坏了。这段代码没有意义。

泛型是编译器想象的虚构:编译器使用它来将事物联系在一起。这不是“幸存”编译的东西- T 之后就消失了。编译器在编译期间使用它进行一些额外的错误检查和便利,然后它就消失了。因此,您编写的任何泛型都必须为编译过程添加一些有用的东西,否则它会被误导和/或只会让事情变得混乱。

换句话说,任何类型变量都必须至少在两个地方使用——它们的重点是链接事物。例如,在这个方法中:

public <T> T identity(T in) { return in; }

我们T在两个地方使用了:这个方法将第一个(也是唯一的)参数的类型链接到返回类型,并且说不管这个方法被调用,它在两个地方都是相同的类型。这很有用。在您的代码中, T 仅在一个地方使用:ApiResponse<T>. 这使它完全无用。

无论您认为它<T>在做什么(可能)都不是它在做什么。因为它在这里有效地做的是手动放弃类型系统,而你真的不想这样做。

大概Type returnType是链接到返回的ApiResponse对象。您的代码不是特别清楚,所以这是一个有根据的猜测,但是,大概Type returnType是 eg String.class,这应该会导致返回一个ApiResponse<String>对象。

不是这段代码的作用。此代码将适应仅在编译时禁用类型检查。你可以写:

ApiResponse<Integer> thisMakesNoSense = execute(someCall, String.class);

即使您确实想要这样,您也不会从编译器错误中受益。

你不应该Type在这里使用。除非您正在编写注释处理器或反射库(而且您通常都不想要​​),否则您几乎不应该使用它。

您在这里有两个选择:

  • ApiResponse 类型始终是一个简单的类。即它是一个TwitterMentions类或aNewTweetResponse或诸如此类,而不是例如aList<Tweet>或an int。在这种情况下,您可以只使用Class<T>而不是 Type: public <T extends TwitterType> ApiResponse<T> execute(Call call, Class<T> twitterType) {}。这将 [A] 保证您只能execute使用作为 TwitterType 的某个子类型的 T 调用,并且 [B] 正确地将事物联系在一起:传递 egTwitterMentions.class执行意味着它返回 a ApiResponse<TwitterMentions>,而不是可以是 schroedingers cat 响应分配给你想要的任何东西,即使它没有意义。

  • 或者,如果 ApiResponse 可以是 a List<Tweet>,它应该是一个超级类型令牌(您可以在网上搜索“java 超级类型令牌”,它需要一些解释并且存在许多不错的教程),这样您就可以绑定泛型一起运行时检索可能需要的类型handleResponse

对于更简单Class<T>的情况:

public interface TwitterType {
    public String getRequestId();
}

public class TwitterMentions implements TwitterType {
    @Override public String getRequestId() {
      ..
    }

    public List<TwitterMention> getMentions() {
      ..
    }
}

public <T extends TwitterType> ApiResponse<T> execute(Call call, Class<?> returnType) throws ApiException { ... }

推荐阅读