小编典典

该方法如何推断类型

java

下面的方法完美无瑕

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

但是我没有指定此方法中的是什么。编译器如何将fromJson方法返回的值分配给list未指定类型i 的变量?

我只是测试了答案的有效性,指出了<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()


阅读 146

收藏
2020-11-16

共1个答案

小编典典

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

没有。泛型方法不推断其泛型类型-这就是为什么T称为 类型参数 的原因。方法的 调用者 提供的类型参数T。如果这样做,编译器 可能
会根据方法调用的参数和目标类型的上下文来推断出它。

例如:

Set<String> c = Collections.emptySet();

emptySet声明类型参数T,不带参数,并返回Set<T>。在此,编译器推断TString基于目标类型Set<String>

另一个例子:

Collections.singleton("asdf");

singleton声明一个类型参数T,使用T,然后返回Set<T>。在这里,没有目标类型,但是编译器推断TString基于参数"asdf"

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

Set<String> c = Collections.<String>emptySet();
Collections.<String>singleton("asdf");

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

public <T> void fromJsonArray(String jsonString, Type tToken)

fromJsonArray声明类型参数T,但不返回与类型相关的任何东西T或接受与类型有关的参数T。在调用时fromJsonArray,编译器没有从中进行推断的信息TObject除非使用类型见证,否则其类型参数将默认为其上限。

myObj.<String>fromJsonArray(jsonString, tToken);

但这没关系,因为<String>对方法调用或其编译的行为没有影响。T是没有意义的*,可以从的声明中删除fromJsonArray

编译器如何将fromJson方法返回的值分配给list未指定类型i 的变量?

这是来源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已被擦除。您可以看到代码抑制了这样做的警告,因为通常这是一个坏主意。通过不限制T基于方法参数的内容,Gson代码已有效地将其控制权转让给了调用者。如果您写:

List<String> list = g.fromJson(jsonString, tToken);

但用tToken表示HashSet<String>,您将ClassCastException在运行时在该行上得到一个。更糟糕的是,如果tToken表示的话ArrayList<Integer>,它甚至不会在那条线上失败,因为JVM只会看到List并允许分配发生。ClassCastException一旦您的代码尝试将列表的Integer元素处理为Strings
,便会在以后的某个时间抛出A (并且此异常会导致调试混乱)。

因此,要回答有关分配的问题,编译器可让您将结果分配给所需的fromJson任何对象。正确无误取决于您。

您可能会问, 为什么Gson会进行未经检查的强制转换并允许使用不安全的代码? 答案是由于语言限制,这很方便。他们的其他签名更安全:

public <T> T fromJson(String json, Class<T> classOfT)

但是没有办法用Class-
来表示通用类型List<String>.class。只有一个Type可以做到这一点,它本身并不是通用的。fromJson可能需要一个TypeToken<T>,但是还有其他方法可以获取Type,因此这是限制性的。

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

2020-11-16