首页 > 解决方案 > 类型推断在 java 中究竟是如何工作的?

问题描述

我正在尝试创建如下方法:

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(更准确地说,序列化和反序列化使用谷歌的Gson库,初始对象是 的实例Integer)。因此,我得到ClassCastException.

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

阐述:

经过一番研究,这个常见问题解答帮助我更好地理解了这个话题。

标签: javagenericstype-inference

解决方案


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

所以,这是有效的:

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

但这也是有效的:

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

getInstance方法所看到的只是: 它对ormKey的任何内容一无所知。所以这在两种情况下都必须返回相同的结果——但至少其中一个是错误的(或者它们都在返回)。<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>作为方法参数。在呼叫站点上变得非常麻烦;但这就是您使用具有已擦除类型的语言所付出的代价。


推荐阅读