java 中的类型推断究竟是如何工作的?
How exactly type inference is working in java?
我正在尝试创建如下方法:
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()
方法returnsDouble
从未被提及(更准确地说,序列化和反序列化使用google的Gson 库,初始对象是 Integer
的实例)。因此,我得到 ClassCastException
.
那么在这种情况下类型推断究竟是如何工作的,为什么类型见证不起作用?是否可以在不指定 Class<T>.class
作为参数的情况下从外部上下文推断类型?
阐述:
经过一些研究,this FAQ 帮助我更好地理解了这个主题。
你在这里遇到的一般问题是没有任何东西可以限制输出与输入相关。
所以,这是有效的:
Integer i = container.<Integer>getInstance(mKey); // I doubt you even need the <Integer>
但这也有效:
String s = container.<String>getInstance(mKey);
getInstance
方法只看到 mKey
的值:它不知道 <Integer>
或 <String>
的任何信息;所以在这两种情况下这必须 return 相同的结果 - 但至少其中一个是错误的(或者它们都是 returning null
)。
因为getInstance
只接收到mKey
的值,所以这两种情况下的type token是一样的。你可以通过实现你自己的类型标记来看到这一点 class:
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>
作为方法参数注入。它在呼叫站点变得非常麻烦;但这是你使用一种类型被擦除的语言所付出的代价。
我正在尝试创建如下方法:
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()
方法returnsDouble
从未被提及(更准确地说,序列化和反序列化使用google的Gson 库,初始对象是 Integer
的实例)。因此,我得到 ClassCastException
.
那么在这种情况下类型推断究竟是如何工作的,为什么类型见证不起作用?是否可以在不指定 Class<T>.class
作为参数的情况下从外部上下文推断类型?
阐述:
经过一些研究,this FAQ 帮助我更好地理解了这个主题。
你在这里遇到的一般问题是没有任何东西可以限制输出与输入相关。
所以,这是有效的:
Integer i = container.<Integer>getInstance(mKey); // I doubt you even need the <Integer>
但这也有效:
String s = container.<String>getInstance(mKey);
getInstance
方法只看到 mKey
的值:它不知道 <Integer>
或 <String>
的任何信息;所以在这两种情况下这必须 return 相同的结果 - 但至少其中一个是错误的(或者它们都是 returning null
)。
因为getInstance
只接收到mKey
的值,所以这两种情况下的type token是一样的。你可以通过实现你自己的类型标记来看到这一点 class:
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>
作为方法参数注入。它在呼叫站点变得非常麻烦;但这是你使用一种类型被擦除的语言所付出的代价。