I was trying to develop a generic method that could convert a JSON to a generic object that would have another instantiable generic object inside so I could use it as a parser in several places in my application.
I had thought of the following solution, but it doesn't work:
public static <T, K> T<K> jsonToObjectType(String json, TypeReference<T<K>> type) {
// More code
}
Is there any way to be able to perform such a method?
public static <T, K> T<K>
Your T has no bounds, meaning, T can be anything. It could be String.
String has no generics, so how can T<K> make sense? It doesn't, hence, java doesn't let you compile this.
I guess you could conceive of the notion of: "T is some specific type, could be anything, as long as it has exactly 1 generics param", but java doesn't have this, and never will, because that is structural type and java doesn't do that.
However, note that a generics param can be any type, notably include types that are themselves parameterized. Here is a trival example:
public static <T> T coalesce(T a, T b) {
return a == null ? b : a;
}
This method can be used like so:
String a = null;
String b = "Hello";
coalesce(a, b).toLowerCase();
There is absolutely no problem feeding it 2 List<String>, at which point the expression coalesce(listA, listB) would be of type List<String>. And that's just with <T>, not with this <T, K> stuff.
I don't quite know what jsonToObjectType is supposed to do, but assuming that it is supposed to take a string that contains JSON + some super-type-token (you can search the web for that term), which I'm 99.9% certain you have, then just remove K from it all, and you get precisely what you wanted:
public static <T> T jsonToObjectType(String json, TypeReference<T> type) {
// code here
}
and you can call it like so:
String json = "[\"Hello\", \"World!\"]";
List<String> list = jsonToObjectType(json, new TypeReference<List<String>>() {});
and it'll compile without warnings or errors and works.
Related
I am trying to create a method like following:
public <T> T getInstance(String key) {
Type type = new TypeToken<T>(){}.getType();
return deserialize(key, type); }
As far as I am not casting (T) in return statement, I expect compiler to infer type from the outside context or at least from a type witness like here:
Integer i = container.<Integer>getInstance(mKey);
But for some case, getInstance() method returns Double that was never mentioned (to be more precise, the serialization and deserialization are using google's Gson library, and the initial object was an instance of Integer). Hence, I get ClassCastException.
So how exactly does type inferring work in this case and why does the type witness not work? Is it possible to infer type from the outside context without specifying Class<T>.class as an argument?
Elaboration:
After some research, this FAQ helped me to understand the topic much better.
The general problem that you have here is that there is nothing to constrain the output to be related to the inputs.
So, this is valid:
Integer i = container.<Integer>getInstance(mKey); // I doubt you even need the <Integer>
but this is also valid:
String s = container.<String>getInstance(mKey);
All the getInstance method sees is the value of mKey: it doesn't know anything about the <Integer> or <String>; so this has to return the same result in both cases - but at least one of those is wrong (or they are both returning null).
Because getInstance only receives the value of mKey, the type token is the same in both cases. You can see this by implementing your own type token class:
abstract static class TypeToken<T> {
Type getType() {
return getClass().getGenericSuperclass();
}
}
static <T> TypeToken<T> getTypeToken() {
return new TypeToken<T>() {};
}
public static void main (String[] args) throws java.lang.Exception
{
TypeToken<String> stringTypeToken = getTypeToken();
TypeToken<Integer> integerTypeToken = getTypeToken();
System.out.println(stringTypeToken.getType());
System.out.println(integerTypeToken.getType());
System.out.println(stringTypeToken.getType().equals(integerTypeToken.getType()));
}
Output:
Ideone$TypeToken<T>
Ideone$TypeToken<T>
true
In your code, Type type = new TypeToken<T>(){}.getType(); isn't getting the TypeToken for the call site type, it's just getting T. So you're not getting a TypeToken<Integer>.
This works, in the sense that it compiles, but it doesn't work in the sense that it doesn't do what you're trying to get it to do.
The way to do this correctly is to inject the TypeToken<T> as a method parameter. It becomes an awful lot more cumbersome at call sites; but that's the price you pay for using a language with erased types.
Here is an overview of the Java code I have:
// An interface and an implementation class:
public interface MyInterface<T1, T2> { ... }
public class MyImplementation implements MyInterface<int, String> { ... }
// Another class
public class MyClass<T3, T4> { ... }
// Function I want to call
void doStuff(MyInterface i) {
MyClass<int, String> n;
}
// I want to call the function like this:
MyInterface mi = new MyImplementation();
doStuff(mi);
What I can't figure out is if I can get MyClass<int, String> n; to somehow use the generic types from the MyImplementation class passed in to doStuff()? In this case, n would automatically use <int, String> because that's what MyImplementation uses.
Yes, you can.
Let's move away from nebulous hypotheticals and take real classes: Collection<T>, Map<K, V>, and Function<F, T>. Let's say you want to write a method in the Map type (or interface, doesn't matter, a signature is a signature) that takes a 'key converter' (a thing that converts Ks into something else), returning a collection of the something-else, which consists of each key in the map, thrown through the converter, and added to a collection.
class MapImpl<K, V> implements Map<K, V> {
public <T> Collection<T> convertKeys(Function<K, T> converter) {
List<T> out = new ArrayList<T>();
for (K key : keySet()) out.add(converter.apply(key));
return out;
}
}
A lot of concepts are being used here:
The implementation doesn't lock in the types of K and V. You don't just inherit typevars from interfaces you implement, so, MapImpl gets its own K,V which are also used as the K,V for the interface. That covers line 1.
The convertKeys method introduces its own unique typevar, in addition to the K,V it already gets. That's because.. well, that's how the method works: The map's keys have some type, the values have some other type, and this converter converts to some third type. Three types: K, V, and T. A method can introduce new vars just for the method, that's what the <T> is all about in line 2.
Any time you name a type name, if that type is generified, you MUST toss <> after it and put in appropriate things. Or don't put in appropriate things which means: Hey, compiler, figure it out if you can (the so called diamond operator). In your snippet, you use MyInterface i as method param type and that's bad: MyInterface has generics, so it must have <> behind it. In this case, you have to add things because there is no way the compiler can try to figure things out.
Going back to your code, it might look like:
public <K, V> void doStuff(MyInterface<K, V> i) {
MyClass<K, V> n;
}
NB: Remember, generics link things. That final snippet is simply saying: There is a link between the first typearg of the MyInterface part of the 'i' parameter's type, and the first typearg of the MyClass part of the 'n' local variable. I don't know what that type is. I do know it is the same type. Generics are completely useless unless the typevar occurs in 2 or more places.
NB2: If you then want to get real fancy, you start thinking about co/contra/invariance. For example, in the key converter story, if you have a converter that can convert any object into something else that'd be cool too. In fact, a converter that can convert either Ks, or any supertype of Ks, that'd all be suitable. So, really, you end up with: public <T> Collection<T> convertKeys(Function<? super K, ? extends T> converter) {} - but that kind of advanced variance engineering is a nice bonus, feel free to skip those bits in your source until you run into trouble because you didn't take it into consideration.
I am new to generics and read in a article "A parameterized type, such as ArrayList<T>, is not instantiable — we cannot create instances of them".
Full quote, from Java in a Nutshell:
A parameterized type, such as ArrayList<T>, is not instantiable - we
cannot create instances of them. This is because <T> is just a type
parameter - merely a place-holder for a genuine type. It is only when
we provide a concrete value for the type parameter, (e.g.,
ArrayList<String>), that the type becomes fully formed and we can
create objects of that type.
This poses a problem if the type that we want to work with is unknown
at compile time. Fortunately, the Java type system is able to
accommodate this concept. It does so by having an explicit concept of
the unknown type which is represented as <?>.
I understand that it should not be instantiable since the concrete (actual) type is not known. If so, why does the below code compiles without an error?
public class SampleTest {
public static <T> List<T> getList(T... elements) {
List<T> lst = new ArrayList<>(); // shouldn't this line return an error?
return lst;
}
}
I know there is a gap in my understanding of generics here. Can someone point out what am i missing here?
Because T is given as another generic type argument.
It's the whole purpose of generics to make the type parameterizeable. So the caller can specify the type. This can be done in multiple layers: the caller may also be generic and let its caller specify the type.
public static void main(String[] args)
{
foo(7);
}
public static <T> void foo(T value)
{
bar(value);
}
public static <U> void bar(U value)
{
baz(value);
}
public static <V> void baz(V value)
{
System.out.println(value.getClass().getSimpleName());
}
It prints out
Integer
A parameterized type, such as ArrayList<T>, is not instantiable
Means: You cannot create ArrayList of an unknown T. It must be specified at compile time. But it can be done indirectly, by another generic. In your case, it's another T, which will be specified again by the caller of your generic getList.
The wildcard <?> is something different. It is used to specify compatibility. <?> is the syntax to avoid specification of the type. You can use extends to require a basetype or interface. However, you cannot create instances with wildcards.
List<?> list = new ArrayList<String>();
list = new ArrayList<Integer>();
This wouldn't be possible otherwise. It makes most sense when using it in parameter specifications, for instance:
public static int foo(List<? extends Comparable> list)
{
return list.get(1).compareTo(list.get(2));
}
It's very confusing of this book. It assumes that <?> somehow solves the problem that a List with unknown T cannot be instantiated. IMHO, this is rubbish. T must be specified to create an instance.
The code that you mention can compile because the Object "lst" is not actually initialized until the method is called. Since the method knows that it will be getting a var-args argument of type T, it can compile in this scenario. Take the example Wrapper class below for example:
public class Wrapper<T> {
public static <T> List<T> getList(T... elements){
List<T> lst = new ArrayList<>();
for(T element: elements) {
lst.add(element);
}
return lst;
}
}
This code can compile because the method hasn't been called. When the method is called, Type T will be the type that we pass as the var-args argument and the code will have no issue compiling. Lets test this in our main method:
public static void main( String[] args ){
System.out.println(Wrapper.getList("Hi", "Hello", "Yo"));
}
And the output is:
[Hi, Hello, Yo]
However, lets generate a compile-time error to see what the article is talking about within our main method:
Wrapper<T> myWrap = new Wrapper<>();
We are actually trying initialize a generic Object of the Wrapper class in the code above, but is unknown. Since the value for the placeholder will be unknown even when we call the method, it results in a compile-time error, whereas creating a List of type T within the getList method does not cause a compile-time error because it will be initialized with a type when the method is called.
once you call the method -> you are using a concrete value.
the method defines T and later you use it in the return type and the parameter list.
public static <T> List<T> getList(T... elements)
once you will send the first parameter from specific type -> the contract will force you for the next parameters.
List<? extends Object> list = getList("", 1); -> in this case java doesnt find common between string and integer so it uses the most basic connection "Object"
List<String> list2 = getList("test", "test2"); -> here you can see that because all of the parameters are Strings - java find that in common and use it as the T.
The specific passage from the book doesn't make any sense and is wrong. new ArrayList<T>() is perfectly fine provided that we are in the scope of a type parameter named T (either a type parameter of a generic class that we are in, or a type parameter of the generic method we are in).
new ArrayList<T>() can no less be instantiated than new ArrayList<String>() -- both compile to the same bytecode and both just instantiate an ArrayList object at runtime. The object doesn't know anything about its type parameter at runtime, and therefore no knowledge of T at runtime is needed to instantiate it. The type parameter in an instantiation expression of a parameterized type (new ArrayList<T>) is just used by the compiler to type-check the parameters passed to the constructor (there are none in this case) and to figure out the type returned by the expression; it is not used in any other way.
And by the way the method does not need to receive any parameters of type T, or of any type containing T, in order for this to work. A method that receives no arguments can still instantiate and return an ArrayList<T> perfectly fine:
public static <T> List<T> emptyList() {
List<T> lst = new ArrayList<T>();
return lst;
}
Also, the section in the book where this statement appears in doesn't really have anything to do with instantiation -- the section is about wildcards, and wildcards don't really have anything to do with object instantiation at all. So I am not really sure why they are mentioning it (incorrectly) there.
Browsing through Guava libraries I saw this weird signature on a readLines method from Files class:
public static <T> T readLines(File file,
Charset charset,
LineProcessor<T> callback)
I know a little bit about generics in java, but this baffled me.
What does the double T mean here? And why is the first one in angled brackets?
UPDATE: Thanks for the answers. I am still not clear as to why I should use a T inside the brackets. Why for example can't it just be:
public static <> T readLines()
or
pulibc static <K> T readLines()
Or does the java syntax dictate that the SAME letter must be used?
Now this is even wierder:
static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
how can a method have a generic-return type and be void?
It's a generic method -- the T is called a type parameter, and can represent any type. So if I have a method with this signature:
public <T> T foo(T[] bar)
I can call it on any array, and it will return a single object of the same type. If I pass it a String array, I'll get back a String, and so on. More information in the Sun tutorials for "generic methods".
Edit: In answer to your updated question, bear in mind that the first <T> isn't part of the return type: it's just an indicator that T is a type parameter. So look at the example you quoted:
static <T> void fromArrayToCollection(T[] a, Collection<T> c)
That just means that fromArrayToCollection will accept any array and any collection, but that they must be an array and collection of the same type. So you can pass in a String[] and a Collection<String>, or an Integer[] and a Collection<Integer>, but not a String[] and a Collection<Integer>. No matter what type you put in for T, the method returns nothing.
The first T inside the angle brackets mean that the method itself is generic. The second T is the return type. T can be any type within its bounds. In this case, T has no bounds.
T will be determined at the call site, and in this case, inferred from the LineProcessor<T> parameter.
I am still not clear as to why I should use a T inside the brackets. Why for example can't it just be:
public static <> T readLines()
or
public static <K> T readLines()
Or does the java syntax dictate that the SAME letter must be used?
The <T> or <K> is the type parameter. If you write <K> T, then the T isn't a type parameter - rather, you're using the specific class T. This won't work if you don't have a class that's literally named T in scope.
Now this is even wierder:
static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
how can a method have a generic-return type and be void?
It doesn't; the <T> is not a "generic return type", it's just the type parameter to the method. You're saying that the method is generic, and T is the type parameter. The return type of the method is void.
Instead of being generic at class level only the method readLines uses generics.
The first <T> declares the generic types used by the method
The following T is the return type.
The first one uses the same syntax as a generic class to declare the generic types. Instead you could write
class Generic <T>
{
public static T readLines(File file,
Charset charset,
LineProcessor<T> callback)
}
This however would make all instances of the class generic.
Extended Example:
public static <ElementType,ListType extends List<ElementType>> ListType add(ListType list,ElementType elem)
{
list.add(elem);
return list;
}
ArrayList<String> = add(add(new ArrayList<String>(),"Hello"),"World");
The method adds a given Element to a List and returns the List.
The method uses two generic Types one for the elements of the list and one for the list itself.
The names used are nothing special, using a T for a generic type is like using i for an integer.
ElementType is the name used for the generic Type of the elements (any valid variable name / identifier could be used)
ListType is the name for the generic list Type, the classes used have to extend/implement List for the ElementType.
The example calls the method with:
ElementType = String
ListType = ArrayList
which would result in
public static ArrayList<String> add(ArrayList<String> list, String elem)
Bloat end :-)
This is a generic method.
Actually there are three Ts, the third on LineProcessor<T> specifies T when you use the method.
"When you declare a type parameter for the class, you can simply use that type any place that you'd use a real class or interface type. The type declared in the method argument is essentially replaced with the type you use when you instantiate the class.
If the class itself doesn't use a type parameter, you can still specify one for a method, by declaring it in a really unusual (but available) space-before the return type, This method says that T can be "any type of Animal"."
Can you explain?
What it means is that in a generic class, you can write methods like so:
public T doSomething () {
}
Note that the return type is 'T'.
At compile-time, the return type of that method will be whatever you have passed to the generic class when you instantiated it.
class Zoo<T> {
static void putAnimal(T animal) {
// do stuff
}
}
Zoo<Ape> apeZoo = new Zoo<Ape>(); // you can now put apes (and chimps) here
Zoo<Reptile> monkeyZoo = new Zoo<Reptile>(); // this zoo takes reptiles
apeZoo.putAnimal(new Chimp());
monkeyZoo.putAnimal(new Tortoise());
For the first paragraph, this is just how generics work for classes. For instance, for list, you can create a list of a generic type, such as integer, e.g.:
ArrayList<Integer> list = new ArrayList<Integer>();
(in real code you'd use List<Integer> of course)
Now ArrayList will be defined as:
public class Arraylist<T> { // implements....
// ...
public T get(int index) {
// ...
}
}
Which is what makes it possible to use the get method on list and get an Integer (because we made a class of type ArrayList<Integer> so T = Integer). Otherwise the compiler would have no idea what types of objects the list was storing and you'd have to get the method to return an Object, which is how it used to be.
What the second paragraph means is that you can add type parameters to methods just as you can to classes. e.g.:
public <T> void noOp(T element) {
// code here
}
This would allow you, for instance, to create a static utility method that returns something of type T. To return the first element of a List of T's:
public static <T> T getFirst (List<T> list) {
return list.get(0);
}
And you could use this method in a strongly typed fashion. Suggestions for better examples welcome. :-)
edit: I just realised I once wrote something that uses this functionality. I was using the JPA API and getting really annoyed at all the times you have to return something (a list, or a single item) from a query, and running into unchecked type warnings because there's no way to infer the type here. If you're like me and trying to avoid warnings in your code, you'd have to suppress the warnings every single time. So I wrote this method to suppress the warnings for me:
#SuppressWarnings("unchecked")
public static <T> List<T> returnResultList(Query query) {
return (List<T>)query.getResultList();
}
Which through the magic of type inference works on:
List<Integer> list = returnResultList(query);