该方法如何推断<T>的类型

Thi*_*thi 10 java generics gson

以下方法完美无瑕

public <T> void fromJsonArray(String jsonString,Type tToken) {
  Gson g = new Gson();
  T list = g.fromJson(jsonString,tToken);
  System.out.println(list);
}
Run Code Online (Sandbox Code Playgroud)

但是我还没有说明这种方法中的<T>是什么.如何将编译器返回的值分配给i未指定fromJson的变量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();
  }
}
Run Code Online (Sandbox Code Playgroud)

再次修改了源并对其进行了测试,结果导致了编译时错误

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);
}
Run Code Online (Sandbox Code Playgroud)

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()

Pau*_*ora 11

该方法如何推断出类型 <T>

它没有.通用方法不会推断它们的泛型类型 - 这就是为什么T称为类型参数.方法的调用者提供了一个类型参数T.当它,它可以通过基于的方法调用的参数和目标类型的上下文编译器来推断.

例如:

Set<String> c = Collections.emptySet();
Run Code Online (Sandbox Code Playgroud)

emptySet声明一个类型参数T,不带参数,并返回一个Set<T>.在这里,编译器推断TString根据目标类型,Set<String>.

另一个例子:

Collections.singleton("asdf");
Run Code Online (Sandbox Code Playgroud)

singleton声明一个类型参数T,取一个T,然后返回一个Set<T>.在这里,没有目标类型,但是编译器推断TString根据的说法"asdf".

但通用类型推断只是一种便利.没有它,我们仍然可以使用类型见证来显式提供类型参数:

Set<String> c = Collections.<String>emptySet();
Collections.<String>singleton("asdf");
Run Code Online (Sandbox Code Playgroud)

这将我们带到您的方法签名:

public <T> void fromJsonArray(String jsonString, Type tToken)
Run Code Online (Sandbox Code Playgroud)

fromJsonArray声明一个类型参数T,但不返回与该类型相关的任何内容T或者获取与之相关的参数T.在调用时fromJsonArray,编译器没有可用于推断的信息T.Object除非使用类型见证,否则其类型参数将默认为其上限:

myObj.<String>fromJsonArray(jsonString, tToken);
Run Code Online (Sandbox Code Playgroud)

但这并不重要,因为<String>它对方法调用或其编译的行为没有影响.T没有意义*可以从声明中删除fromJsonArray.

如何将编译器返回的值分配给i未指定fromJson的变量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;
}
Run Code Online (Sandbox Code Playgroud)

您可以看到它声明了一个任意类型参数T并将反序列化对象强制转换为T.这被称为未经检查的强制转换,因为如果它错了就不会快速失败.那是因为在运行时T已被删除.您可以看到代码禁止执行此操作的警告,因为这通常是一个坏主意.通过不限制T基于方法参数的内容,Gson代码已经有效地将对它的控制权交给了调用者.如果你写道:

List<String> list = g.fromJson(jsonString, tToken);
Run Code Online (Sandbox Code Playgroud)

但是tToken代表HashSet<String>,你会ClassCastException在运行时获得该行.更糟糕的是,如果被tToken表示ArrayList<Integer>,它甚至不会在该行上失败,因为JVM只会看到List并允许分配发生.ClassCastException一旦你的代码试图将列表的Integer元素视为Strings(并且异常将使调试混乱),将在稍后抛出A.

因此,为了回答有关赋值的问题,编译器允许您将结果分配给fromJson您想要的任何内容.这取决于你是否正确.

你可能会问,为什么Gson会做一个未经检查的演员并允许不安全的代码?答案是,这是一种方便,源于语言限制.他们的其他签名更安全:

public <T> T fromJson(String json, Class<T> classOfT)
Run Code Online (Sandbox Code Playgroud)

但是没有办法表示泛型类型Class- List<String>.class例如没有.只有a Type才能做到这一点,而且它本身并不通用.fromJson可能需要一个TypeToken<T>,但还有其他方法来获得Type,所以这将是限制性的.

返回Object并强制调用者进行未经检查的强制转换将更加透明,但Gson开发人员可能希望避免这种"丑陋".