该方法如何推断< T>的类型。 [英] how does the method infer the type of <T>

查看:74
本文介绍了该方法如何推断< T>的类型。的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的方法完美地工作

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

但我没有指定什么< T>在这个方法中。编译器如何将 fromJson 方法返回的值赋给其类型i没有指定的变量 list



我刚刚测试了从方法的返回类型推断出< T> 的答案的有效性。它似乎并没有解决。请查看下面的代码。它甚至不会编译

  import java.util。*; 

class示例{

public List< String> getT(String s){
List< String> list = new ArrayList< String>();
list.add(s);
返回列表;
}

public< T> void test(){
T list = getT(test);
System.out.println(l); ();
}

public static void main(String [] a){
new Sample()。test();


再次修改源代码并对其进行测试,在编译时错误

  public< T>列表与LT; T> getT(T s){
List< T> list = new ArrayList< T>();
list.add(s);
返回列表;
}

public< T> void test(){
T list = getT(test); //不兼容的类型编译错误在这里
System.out.println(list);
}


Sample1.java:13:错误:不兼容的类型
T list = getT(test);
$
required:T
found:List
其中T是一个类型变量:
T extends在方法test()中声明的对象
<
$>

解决方案


该方法如何推断< T>


泛型方法不推断它们的泛型类型 - 这就是为什么 T 被称为类型参数的原因。该方法的 caller T 提供了一个类型参数。当它发生时,它可以由编译器根据方法调用的参数和目标类型的上下文来推断。



例如: / p>

 设定< String> c = Collections.emptySet(); 

emptySet 声明一个类型参数 T ,不接受任何参数,并返回一个 Set 。在这里,编译器根据目标类型 Set >推断 T String ;



另外一个例子:

  Collections.singleton( ASDF); 

singleton 声明一个类型参数 T ,需要一个 T ,并返回一个 Set 。这里没有目标类型,但是编译器根据参数<$ c $推断 T 为 String c>asdf。



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

  Set< String> c =集合。< String> emptySet(); 
Collections。< String> singleton(asdf);

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

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

fromJsonArray 声明了一个类型参数 T ,但不返回与类型 T 有关的任何东西, T 。在从JsonArray 调用时,编译器没有推断 T 的信息。除非使用类型见证,否则其类型参数将默认为其上限 Object

  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);
回报目标;



$ b你可以看到它声明了一个任意的类型参数 T 并将反序列化的对象转换为 T 。这被称为 unchecked cast ,因为如果它错了,它不会快速失败。这是因为 T 已被在运行时擦除。你可以看到代码禁止了这样做的警告,因为这通常是一个坏主意。通过不限制 T 基于方法参数,Gson代码已经有效地将其控制给调用者。如果您写道:

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

tToken 表示 HashSet< String> ,你会在运行时在该行上得到 ClassCastException 。更糟的是,如果 tToken 表示 ArrayList< Integer> ,它甚至不会在该行上失败,因为JVM只会看到一个 List 并允许分配发生。一旦你的代码试图处理列表的 Integer 元素,比如 ClassCastException > String s(这个例外会令人困惑的是调试)。

所以要回答你有关赋值的问题,编译器可以让你赋值 fromJson 到任何你想要的结果。您可能会问,为什么Gson会做一个未经检查的强制转换,并允许不安全的代码?答案是否正确?

这是一种方便,源于语言限制。他们的其他签名更安全:

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

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



返回 Object 并强制调用者未经检查的演员阵容会变得更加透明,但Gson开发者可能想要避免这种丑陋。


The below method works flawlessly

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

But i have not specified what < T > is in this method. how does the compiler assign the value returned by the fromJson method to the variable list whose type i have not specified?

i just tested the validity of the answers stating about <T> being inferred from the return type of the method. It doesn't seem to work out. Please check out the following code. it doesn't even compile

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

modified the source again and tested it, and it resulted in a compile time error

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

解决方案

how does the method infer the type of <T>

It doesn't. Generic methods don't infer their generic types - that's why T is called a type parameter. The caller of the method provides a type argument for T. When it does, it may be inferred by the compiler based on the context of the method call's arguments and target type.

For example:

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

emptySet declares a type parameter T, takes no arguments, and returns a Set<T>. Here, the compiler infers T to be String based on the target type, Set<String>.

Another example:

Collections.singleton("asdf");

singleton declares a type parameter T, takes a T, and returns a Set<T>. Here, there is no target type, but the compiler infers T to be String based on the argument "asdf".

But generic type inference is just a convenience. Without it, we could still use type witnesses to explicitly provide type arguments:

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

This brings us to your method signature:

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

fromJsonArray declares a type parameter T, but doesn't return anything related to the type T or take arguments related to T. At a call to fromJsonArray, the compiler has no information from which to infer T. Its type argument will default to its upper bound Object unless a type witness is used:

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

But this doesn't matter because <String> has no affect on the behavior of the method call or its compilation. T is meaningless* and can be removed from the declaration of fromJsonArray.

how does the compiler assign the value returned by the fromJson method to the variable list whose type i have not specified?

Here is the source of 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;
}

You can see it declares an arbitrary type parameter T and casts the deserialized object to T. This is known as an unchecked cast, because it won't fail fast if it's wrong. That's because T has been erased at runtime. You can see that the code is suppressing a warning about doing this, because it's generally a bad idea. By not restricting what T is based on the method arguments, the Gson code has effectively ceded control over it to the caller. If you wrote:

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

but tToken represented HashSet<String>, you would get a ClassCastException on that line at runtime. Worse, if tToken represented ArrayList<Integer>, it would not even fail on that line, because the JVM would only see a List and allow the assignment to happen. A ClassCastException would be thrown sometime later, once your code tried to treat the list's Integer elements like Strings (and the exception would be confusing to debug).

So to answer your question about the assignment, the compiler lets you assign the result of fromJson to anything you want. It's up to you for it to be correct.

You may ask, Why would Gson do an unchecked cast and allow unsafe code? The answer is that it's a convenience, stemming from language limitations. Their other signature is safer:

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

But there's no way to represent generic types with Class - no List<String>.class for example. Only a Type can do this, and it's not itself generic. fromJson could have required a TypeToken<T>, but there are other ways to obtain a Type, so that would be restrictive.

Returning Object and forcing the caller to do the unchecked cast would have been more transparent but the Gson developers probably wanted to avoid this "ugliness".

这篇关于该方法如何推断&lt; T&gt;的类型。的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆