【问题标题】:How exactly type inference is working in java?类型推断在 java 中究竟是如何工作的?
【发布时间】:2021-07-03 02:06:44
【问题描述】:

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

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

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

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

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

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

阐述:

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

【问题讨论】:

  • 这根本行不通。由于擦除,您无法使用类型变量创建类型标记。
  • @Andy Turner 很好,该方法可以编译。唯一的问题是错误的返回类型。此外,我看到使用其他程序员的泛型类型创建 TypeToken。
  • 我不是说它不编译,我是说它不work;至少,正如您所发现的那样,它不起作用。我应该更准确地说你不能使用类型变量创建类型标记并期望它捕获类型变量以外的任何东西。查看this demonstration:“String”类型标记的“类型”与“Integer”类型标记相同,因为它们捕获的是类型变量,而不是调用站点类型。
  • @Andy Turner 好的,我明白了,这是有道理的。谢谢。

标签: java generics type-inference


【解决方案1】:

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

所以,这是有效的:

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

但这也是有效的:

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

getInstance 方法看到的都是mKey 的值:它对&lt;Integer&gt;&lt;String&gt; 一无所知;所以这在两种情况下都必须返回相同的结果 - 但至少其中一个是错误的(或者它们都返回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&lt;T&gt;(){}.getType(); 没有得到调用站点类型的 TypeToken,它只是得到了 T。所以你没有得到TypeToken&lt;Integer&gt;

从编译的意义上说,这是可行的,但从不做你想让它做的事情的意义上说,它是行不通的。

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

【讨论】:

  • 只是想知道,我能否将 Object 作为一种类型发送到反序列化器,但在 return 语句中将其转换为 (T),这将从外部上下文中推断出来?
  • 我试过后者。此外,我尝试完全不使用泛型,只返回 Object。两者都不起作用,在某些情况下它仍然返回 Double。很奇怪。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-20
  • 1970-01-01
  • 2014-01-20
  • 2019-07-09
  • 1970-01-01
  • 1970-01-01
  • 2021-07-05
相关资源
最近更新 更多