Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how does the method infer the type of <T>

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

like image 661
Thirumalai Parthasarathi Avatar asked Mar 09 '14 08:03

Thirumalai Parthasarathi


People also ask

How does Python infer types?

Some languages require every symbol to be explicitly typed. Python allows a symbol to be bound to different values at runtime, so its type can change over time. A symbol's type doesn't need to be declared statically.

What is T in Java method?

The <T> parameter tells the compiler that both List s being passed as well as the List returned must be of the same generic type.

What is inferred type in Java?

Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable.

What is infer generic type arguments?

Type inference represents the Java compiler's ability to look at a method invocation and its corresponding declaration to check and determine the type argument(s). The inference algorithm checks the types of the arguments and, if available, assigned type is returned.


2 Answers

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".

like image 196
Paul Bellora Avatar answered Nov 01 '22 16:11

Paul Bellora


T list = g.fromJson(jsonString,tToken);

It's inferred from the return type of g.fromJson().

like image 42
user207421 Avatar answered Nov 01 '22 14:11

user207421