类型推断在 Java 中究竟是如何工作的?

时间:2021-04-07 09:18:40

标签: java generics type-inference

我正在尝试创建一个如下所示的方法:

public <T> T getInstance(String key) {
   Type type = new TypeToken<T>(){}.getType();
   return deserialize(key, type); }

就我没有在 (T) 语句中强制转换 return 而言,我希望编译器从外部上下文或至少从像这里这样的类型见证中推断类型:

Integer i = container.<Integer>getInstance(mKey);

但在某些情况下,getInstance() 方法返回从未提及的 Double(更准确地说,序列化和反序列化使用的是 google 的 Gson 库,并且初始对象是一个Integer 的实例)。因此,我得到 ClassCastException

那么在这种情况下类型推断究竟是如何工作的,为什么类型见证不起作用?是否可以在不指定 Class<T>.class 作为参数的情况下从外部上下文推断类型?

细化:

经过一些研究,this FAQ 帮助我更好地理解了该主题。

1 个答案:

答案 0 :(得分:0)

您在这里遇到的一般问题是没有什么可以限制输出与输入相关。

所以,这是有效的:

Integer i = container.<Integer>getInstance(mKey);  // I doubt you even need the <Integer>

但这有效:

String s = container.<String>getInstance(mKey);

getInstance 方法看到的只是 mKey 的值:它对 <Integer><String> 一无所知;所以这必须在两种情况下返回相同的结果 - 但至少其中一个是错误的(或者它们都返回 null)。


因为 getInstance 只接收 mKey 的值,所以两种情况下的类型标记是相同的。你可以通过实现你自己的类型标记类来看到这一点:

abstract static class TypeToken<T> {
    Type getType() {
        return getClass().getGenericSuperclass();
    }
}

static <T> TypeToken<T> getTypeToken() {
    return new TypeToken<T>() {};
}

public static void main (String[] args) throws java.lang.Exception
{
    TypeToken<String> stringTypeToken = getTypeToken();
    TypeToken<Integer> integerTypeToken = getTypeToken();
    System.out.println(stringTypeToken.getType());
    System.out.println(integerTypeToken.getType());
    System.out.println(stringTypeToken.getType().equals(integerTypeToken.getType()));
}

输出:

Ideone$TypeToken<T>
Ideone$TypeToken<T>
true

在您的代码中,Type type = new TypeToken<T>(){}.getType(); 没有获得呼叫站点类型的 TypeToken,它只是获得了 T。所以你没有得到 TypeToken<Integer>

这行得通,因为它可以编译,但它行不通,因为它没有做你想让它做的事。

正确执行此操作的方法是将 TypeToken<T> 作为方法参数注入。在呼叫站点上它变得非常麻烦;但这就是您使用具有擦除类型的语言所付出的代价。