以下方法完美无缺
public <T> void fromJsonArray(String jsonString,Type tToken) {
Gson g = new Gson();
T list = g.fromJson(jsonString,tToken);
System.out.println(list);
}
但我没有说明&lt; T>是在这种方法。编译器如何将fromJson
方法返回的值赋给i型未指定的变量list
?
我刚刚测试了答案的有效性,说明从方法的返回类型推断<T>
。它似乎没有成功。请查看以下代码。它甚至没有编译
import java.util.*;
class Sample {
public List<String> getT(String s) {
List<String> list = new ArrayList<String>();
list.add(s);
return list;
}
public <T> void test(){
T list = getT("test");
System.out.println(l);
}
public static void main(String[] a) {
new Sample().test();
}
}
再次修改了源并对其进行了测试,结果导致编译时错误
public <T> List<T> getT(T s) {
List<T> list = new ArrayList<T>();
list.add(s);
return list;
}
public <T> void test(){
T list = getT("test"); //incompatible types compilation error here
System.out.println(list);
}
Sample1.java:13: error: incompatible types
T list = getT("test");
^
required: T
found: List
where T is a type-variable:
T extends Object declared in method test()
答案 0 :(得分:10)
该方法如何推断
的类型<T>
没有。通用方法不会推断它们的泛型类型 - 这就是为什么T
被称为类型参数的原因。该方法的调用者为T
提供了一个类型参数。如果是这样,编译器可以根据方法调用的参数和目标类型的上下文来推断 。
例如:
Set<String> c = Collections.emptySet();
emptySet
声明类型参数T
,不带参数,并返回Set<T>
。在这里,编译器根据目标类型T
将String
推断为Set<String>
。
另一个例子:
Collections.singleton("asdf");
singleton
声明类型参数T
,取T
,然后返回Set<T>
。这里没有目标类型,但编译器根据参数T
推断String
为"asdf"
。
但是泛型类型推断只是一种便利。没有它,我们仍然可以使用类型见证来显式提供类型参数:
Set<String> c = Collections.<String>emptySet();
Collections.<String>singleton("asdf");
这将我们带到您的方法签名:
public <T> void fromJsonArray(String jsonString, Type tToken)
fromJsonArray
声明类型参数T
,但不返回与类型T
相关的任何内容,也不会引用与T
相关的参数。在调用fromJsonArray
时,编译器没有可用于推断T
的信息。除非使用类型见证,否则其类型参数将默认为其上限Object
:
myObj.<String>fromJsonArray(jsonString, tToken);
但这并不重要,因为<String>
对方法调用或其编译的行为没有影响。 T
无意义*,可以从fromJsonArray
的声明中删除。
编译器如何将
fromJson
方法返回的值分配给i未指定的变量list
?
以下是Gson.fromJson(String, Type)
的来源:
@SuppressWarnings("unchecked")
public <T> T fromJson(String json, Type typeOfT) throws JsonParseException {
StringReader reader = new StringReader(json);
T target = (T) fromJson(reader, typeOfT);
return target;
}
您可以看到它声明了一个任意类型参数T
并将反序列化的对象强制转换为T
。这称为未经检查的强制转换,因为如果错误,它不会快速失败。那是因为T
在运行时已经erased。您可以看到代码禁止执行此操作的警告,因为这通常是一个坏主意。通过不限制T
基于方法参数的内容,Gson代码已经有效地将对它的控制权交给了调用者。如果你写了:
List<String> list = g.fromJson(jsonString, tToken);
但是tToken
代表HashSet<String>
,您将在运行时获得该行的ClassCastException
。更糟糕的是,如果tToken
表示ArrayList<Integer>
,它甚至不会在该行上失败,因为JVM只会看到List
并允许分配发生。一旦您的代码尝试处理列表的ClassCastException
元素(例如Integer
s(并且异常会使调试混乱),将在稍后的某个时间抛出String
。
因此,为了回答有关赋值的问题,编译器允许您将fromJson
的结果分配给您想要的任何内容。这取决于你是否正确。
你可能会问,为什么Gson会做一个未经检查的演员并允许不安全的代码?答案是,这是一种方便,源于语言限制。他们的另一个签名更安全:
public <T> T fromJson(String json, Class<T> classOfT)
但是没有办法用Class
表示通用类型 - 例如没有List<String>.class
。只有Type
可以做到这一点,而且它本身并不通用。 fromJson
可能需要TypeToken<T>
,但还有其他方法可以获得Type
,因此这将是限制性的。
返回Object
并强制调用者进行未经检查的强制转换会更加透明,但Gson开发人员可能希望避免这种“丑陋”。
答案 1 :(得分:3)
T list = g.fromJson(jsonString,tToken);
从g.fromJson().