0

I try to write a generic data loader. Here is the loader logic (code snippet 1 (CS1)):

public class Loader<T> {

    private ObjectMapper objectMapper = new ObjectMapper();

    public T getData(String testDataPath , Class<T> resultClassType) throws IOException {
        return objectMapper.readValue(new File(testDataPath), resultClassType);
    }
}

And here is the code snipper where I use the loader (code snippet 2 (CS2)):

 String[] stringArray;

 //Todo something
 
 stringArray = new Loader<String[]>().getData("<path>", String[].class);

My first question is: if I pass the type information here in CS2: new Loader<String[]>() why I can't use this generic information here: return objectMapper.readValue(new File(testDataPath), new T().getClass());?

And at this point I got confused of terms T, Class<> and other type related classes which are allowed to pass as second parameter in the readValue function in objectMapper (Class<>, JavaType, TypeReference, ResolvedType).

So can someone explain me why I can't use the T as I tried and what are the differences between Class<>, JavaType, TypeReference, ResolvedType?

Thx!

HowToTellAChild
  • 623
  • 1
  • 9
  • 21
  • 2
    `new T()` is impossible to executed at runtime because `T` is *erased* https://docs.oracle.com/javase/tutorial/java/generics/erasure.html. To preserve *information* about T at runtime we use `Class`. BUT in case where `T` is generic itself, like `List` the `Class>` does NOT preserve `` part (again, type erasure) but only `List` part. To preserve full generic type (including those *inner* generic types) we use fact that fields lets us get full information about its type: https://stackoverflow.com/questions/1942644/get-generic-type-of-java-util-list/1942680 – Pshemo Dec 25 '22 at 12:19
  • Related: [TypeReference>() { }](https://stackoverflow.com/q/51767347), [Does Jackson TypeReference work when extended?](https://stackoverflow.com/q/19283606) – Pshemo Dec 25 '22 at 13:00
  • Also take a look at [How to deserialize generic List with Jackson?](https://stackoverflow.com/questions/61150873/how-to-deserialize-generic-listt-with-jackson/61154659#61154659) – Michał Ziober Dec 28 '22 at 23:38

1 Answers1

0

T is nothing but the placeholder for the actual type, which will be provided at runtime. Therefore, the compiler has no clue T is, and consequently would not allow you to do things like T.class, or new T(). But compiler can help you to ensure that where you expect to operate with type T you'll really get T, i.e. you code type-safe.

Regarding the Jackson's TypeReference it's useful for Collections and parametrized classes, for instance:

new TypeReference<List<String>() {}
new TypeReference<Foo<Bar>() {}

If you were using Class<T>, you'll not be able to provide information about parameters of the type: Class<List.class>, Class<Foo.class>.

Therefore, Class<T> is handy only when you're parsing a non-generic object of type T.

For more information on Generics, refer to the Java language specification:

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
  • Okay, so if I use type T than this is for some kind of syntax check, right? I mean compiler can review the places where I replaced the type and inform me whether I was stupid or not. But this is just the place where I replace the T with some type. The place where I am holding type T can not access any further information of the given type. Am I right? – HowToTellAChild Dec 26 '22 at 20:18
  • @HowToTellAChild Your understanding is correct, Generic type is compile-time type-safety mechanism. Compiler does it's job verifying that where you're expecting `T` you can really get `T`, and then generic information [gets erased](https://docs.oracle.com/javase/specs/jls/se17/html/jls-4.html#jls-4.6) (compiler replaces it with casts byte-code, so `List` and `List` would turn into `List` and transformation into required type would be performed via safe checkcasts, they are safe because compiler has generated them after verifying that target types are correct). – Alexander Ivanchenko Dec 26 '22 at 20:37
  • @HowToTellAChild And since `T` is not a real type, but just a placeholder, alias for the type that would be provided at Runtime, the compiler has no clue what is `T`, but it knows that it's a distinct type, which it would consider to be different from `BigDecimal`, `Runnable`, or other type parameters it might encounter in the code like `U`, `V`, etc. – Alexander Ivanchenko Dec 26 '22 at 20:42