I'm using reflection to walk the field members of a class and I need to know for List<> subclasses, what the generic type parameters are.
Given a field that has a type that is a subclass of List, how can I tell in a generic way what the type parameters of List<> are?
For example:
class X<T> {
List<String> x1; // String
ArrayList<String> x2; // String
SubclassOfArrayListString x3; // String
List<?> x4; // error
List<T> x5; // error
}
class SubclassOfArrayListString extends ArrayList<String> {
// ...
}
NOTE: I added <T> to X above to illustrate that there might be cases where there isn't a correct answer - it has nothing to do with the problem, except being something to consider when answering.
You can't because generic type information is lost on compilation (*). That's also the reason you cannot create an array of some generic type T at runtime.
At runtime, every List<T>is again a raw type List -- you could even add an Integer to something declared as List<String>, generics won't and can't prevent that [Edit: using unchecked casts or a widening cast to a raw type; this will result in (suppressable) compiler warnings but no errors].
(*) Edit: I learned some new and stand corrected, certain type parameters (implementors of GenericDeclaration like Class, Constructor, Field and the return, parameter and exception types of Method) will be retained in the byte code and can be accessed at runtime using Field.getGenericType() and similar accessors.
public class X<T extends List> {
T field;
}
Is that what you need?
Read this tutorial about Generics : Generics Tutorial. This should clear things up.
This pdf has also good examples at the end of the wildcard usage as well as the extends ans super keywords. PDF Doc
Basically, Use Container<? extends A> when you want to read from the container
Use Container<? super A> when you want to write to the container.
Related
This question is specifically about nested lists which is a static field of the generic class. If I am maintaining the logs in a static variable inside my class using the code:
static List<List<? extends Number>> myLog = new ArrayList<List<? extends Number>>();
myLog is a list of list whose element is of type that extends Number. Here, instead of extending Number, I might want to use a generic type <? extends T>.
I understand that type variables exist only at compile time, and all instances of a generic class has the same run-time class, due to type erasure. Then, how is the nested list (List interface) within the top-level list (ArrayList) is determined at compile time about its actual implementation type (ArrayList,LinkedList, ...)? I find it a bit complex, as a couple of things are happening at the same time. Any help is appreciated.
EDIT: So, for example, in my code I have a method sortList which sorts a list of numbers, and it will also log that list of numbers in myLog list, like:
public void sortList(List<? extends Number> inList) {
// do something
myLog.addLast(inList);
// do some more
}
Now, instead of using <? extends Number>, I might want to use <? extends T> in a generic class. So when creating the static field myLog, when type erasure happens, how does compiler determine the type of the nested list. I hope I am a bit clearer this time.
(as said in comments)
Type erasure hapens after the type has been determined and check.
In your code you described the type of myLog and the type of inList, so the compiler can determine at compile time if the types matches (and they do in both case) and thus, enforce the types.
Then type erasure happens, and types are lost for runtime (if you get myLog as a List you can add whatever you want, juste like if you described it as static List myLog).Well they're not completely lost: meta datas/refelction can give you these inforamtions, but they're not enforced anymore, like the compiler did.
Not new to java but You may consider Me as new to Generics and having through various Confusion such that
A) Is It necessary to add generics identifier to Method IF Method contains any argument like myMethod(List <T> prm_ObjT, List<? extends Object>) Or Any Such type of Arguments .
I Have tried this example to learn this Concept. And write multiple Variety of this Method
public static void test(List<T> set)
Error : cannot find symbol T :- public static void test(List<T> set)
public static <T extends Object> void test(List<T> list) // Works Fine to me
public static <T> void test(List<T> list) // Works Again fine to me
public static <? extends Object>void test(List<?> set)
Error : error: <identifier> expected
public static <? extends Object>void test(List<?> set)
class Ideone
{
private static List<String> obj_larr= new ArrayList<String>();
static {
obj_larr.add("Vikrant");
obj_larr.add("Nakul");
obj_larr.add("Vishwas");
obj_larr.add("Neeraj");
obj_larr.add("Wahid");
}
public static <T extends Object> void test(List<T> list){ //Works Fine To me.
System.out.println(list);
}
public static void main (String[] args) throws java.lang.Exception
{
test(obj_larr);
}
}
Question :- Please explain each Situation and Provide all possible Combination of Identifier that can attach with a Method() If Method Generic Argument is passed in different ways ..?
Java Generics are confusing and somewhat illusive. There is also some terminology that one needs to use precisely. Usually, applying first principles works, but there are some hairy cases where the line noise in the Java source code becomes rather too much. Fortunately, your confusion is in understandable parts of Java Generics.
First of all, all your questions are with respect to generic methods. In Java, you have both generic classes and generic methods. The thing of (generic) essence here is the so called type parameter. The weird looking things enclosed inside <> are called type parameters.
The usefulness of generic methods is evident when you have a particular case at hand. My favorite example of this is from the JDK library itself, in the java.util.Arrays class:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
Here, the generic method is taking a variable list of arguments of any type and returning an ArrayList containing those elements. An invocation of this method is:
List<Integer> intList = Arrays.asList(1, 2, 3);
or
List<String> myFriends = Arrays.asList("Larry", "Moe", "Curly");
See how generic definition of Arrays.asList helps here, the compiler is able to assign Integer to the type parameter T in the first case, whereas String in another case!
Understanding generics using concrete examples (rather than contrived examples you cite) like the above helps.
There's a lot going on here, but one way to explain this is to look at it from a compiler's standpoint. Here's an attempt to explain why compilation succeeds or fails in the cases you have cited:
When it tries to compile a method like public static void test(List<T> set), it gathers that it is a static method that takes a List of some type parameter T. But what is T? Compiler wants you to specify that it is a generic type parameter (let's say it's just the "syntax" of generic method declaration). That's what you do when you say public static <T> void test(List<T> set) and compiler is happy.
In the second declaration, you do specify that and compiler is happy. It's important to note that like any other Object in Java, the type parameter itself also needs to be an Object and like (when your class is just a java.lang.Object) you don't have to specify public class MyClass extends Object, you don't have to specify that T extends Object if that's the only restriction you have. So, public static <T extends Object> void test(List<T> list) is equivalent to public static <T> void test(List<T> list). You might argue why <T> is needed in the generic method declaration, but you may read the language specification for details, since the devil is in the details here.
As we said above in 2), 2) and 3) are equivalent.
The type parameter, if specified, must have a name. In this declaration you are considering the so-called wild-card and that complicates things further. But leaving that aside, like the compiler is complaining, it does not find the generic type declaration in your method declaration valid because it wants to know the name (or, identifier) for the type parameter.
I am not good at generic either. Here is my way to remember the syntax:
When using the generic type T in a method, it can be a parameter or a return type. So you will have to declare it before the return type.
The wildcard ? means we don't care about the type, so you use it directly.
You'll find a complete treatment of type calculus (why the type system is mathematically sound) in Benjamin Pierce's excellent book Types and Programming Languages (TAPL).
An important first thing to understand is that type variables name types, which in Java are always represented as classes.
Before Generics
Using your method signature, let's consider what it looked like before generics in Java: public static void test(List list). That shouldn't be confusing at all.
However, note that the "naked" List can hold any objects at all. A more precise way to say that: it can hold any subclass of Object and we have no way of constraining types added to the list, e.g., a list containing both an Integer and a String.
List list = new ArrayList();
list.add("hello world!");
list.add(new Integer(1));
test(list);
That's perfectly legal Java syntax, even today (with compiler warnings). But notice that the test method must handle any possible type provided inside the list.
What's needed is a way to constrain the types that can be placed into the list and have the compiler enforce that to the best of its ability.
Generically
We can use the syntax public static void test(List<Object> list) to represent the same thing, but we still haven't constrained the values of the list.
Type Constants and Type Variables
Notice above where we specified List<Object>, that Object is a type constant. The only thing it describes is instances of the exact type Object and that's really all we can know about items the list contains.
What we need is a way to specify an unknown type when the List class is being compiled. There's no way that the JDK, which implements the List class, can know at its compile time what you might want to put in it.
Furthermore, we need more flexibility than that. We'd like to be able to specify type constraints in 3 different flavors:
some type that we'll name T, exactly (a.k.a. invariant), with syntax <T>;
some type that we'll name T and all of its subtypes (a.k.a. covariant), with syntax <? extends T>;
some type that we'll name T and all of its supertypes (a.k.a. contravariant), with syntax <? super T>.
That's where type variables come from. They represent unknown types at compile time, just as normal variables represent unknown values at compile time.
In order to get all 3 constraint flavors, the compiler treats type variables for Java methods using the syntax above. You cannot use the second or third syntax for generic variable declarations (a List<Number> can hold values of type Number or any of its subclasses without extra syntax).
Note that in the type calculus presented in TAPL, T is both the first subtype of itself, and also the first supertype of itself. So saying ? extends T or ? super T implicitly includes T itself.
More on type variance at https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
Back to Your Examples
public static void test(List<T> set) fails because the compiler is looking for a type (class named T), which isn't defined anywhere.
public static <T extends Object> void test(List<T> list) is correct syntax but more verbose than it needs to be. Idiomatically, it's the same as
public static <T> void test(List<T> list)
Here, the test method still receives values of type Object, because there's no further constraint on T.
public static <? extends Object> void test(List<?> set) fails because ? is not a valid type variable name (same restrictions as for Java class names).
public static void test(List<?> set) works just fine.
Notes
In Java, generics are only a feature of the compiler. There were no JVM changes. Therefore the bytecode method signatures still use Object, and the compiler emits cast instructions where needed.
Because of (1), you have to consider type erasure, but that's another question for another day.
I had a issue where (to simplify):
public void method(List<List<?>> list){...}
gave me a compilation error when called with:
method(new ArrayList<List<String>>()); // This line gives the error
After reading a similar thread, I understood that it would work if I were to rewrite the method signature as:
public void method(List<? extends List<?>> list){...}
Now, my question is, why does the following work then?
public <T> void method(List<List<T>> list){...}
Confusions do come when you deal with multi-level wildcard syntax. Let's understand what those types exactly mean in there:
List<List<?>> is a concrete parameterized type. It is a heterogenous collection of different types of List<E>. Since List<?> represent a family of all the instantiation of List, you can't really pass an ArrayList<List<String>> to List<List<?>>. Because, nothing stops you from adding a List<Integer> to it inside the method, and that will crash at runtime, had compiler allowed it.
List<? extends List<?>> is a wildcard parameterized type. It represents a family of different types of List<E>. Basically, it might be a List<ArrayList<String>>, List<LinkedList<Date>>, so on. It can be a list of any type that extend from a List<?>. So, it will be safe to pass a ArrayList<List<String>> to it, the reason being, you won't be allowed to add anything, but null to the list. Adding anything to the list will be a compile time error.
As for List<List<T>>, it is again a concrete parameterized type. And since you're dealing with a generic method now, the type parameter will be inferred to be the type that is passed for it. So, for an ArrayList<List<String>>, type T will be inferred as T. A generic method deals with the types that are declared with it. So, there is only a single type T here. All the lists you get out of List<List<T>> will certainly be a List<T> for any type T. So, it's a homogenous collection of that type of List. Inside the method, you can not add any arbitrary List<E> to the List<List<T>>, because the compiler doesn't know whether that type E is compatible with T or not. So, it is safe invocation.
Related:
Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
Java HashMap nested generics with wildcards
What are multi-level wild cards? Confusion in syntax
When to use generic methods and when to use wild-card?
I think I found the answer in Angelika Langer's generics FAQ, "Case Study #3":
If a method signature uses multi-level wildcard types then there is always a difference between the generic method signature and the wildcard version of it. Here is an example. Assume there is a generic type Box and we need to declare a method that takes a list of boxes.
Example (of a method with a type parameter):
public static <T> void print1( List <Box<T>> list) {
for (Box<T> box : list) {
System.out.println(box);
}
}
Example (of method with wildcards):
public static void print2( List <Box<?>> list) {
for (Box<?> box : list) {
System.out.println(box);
}
}
Both methods are perfectly well behaved methods, but they are not equivalent. The generic version requires a homogenous list of boxes of the same type. The wildcard version accepts a heterogenous list of boxes of different type. This becomes visible when the two print methods are invoked.
The basic reason is that List<List<?>> is not a superclass of List<List<String>>.
A List<List<?>> could contain a List<Integer> and a List<String> for example.
The generic types must match exactly, otherwise you could get erroneous assignments made.
I have a Generic Class Factory class that has two methods one utilizes the Class generic T value and the other only uses its own method generic definitions.
public class GenericClassFactory<T extends ClassMatchable> {
public <E, K> E newObject(ClassMatcher<E, K> matcher, K key, String packageName){...}
public <K> T newObject(K key, String packageName){...}
}
The method that utilizes the T generic works fine but when I want to use the other method that doesn't care what the T generic is it won't use the Generic E it will just return an Object and then I have to type cast it.
Data data = new GenericClassFactory().newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl");
This has compile errors because it wants me to typecast it to (Data). If I pass the GenericClassFactory a valid Class Generic it will work. Its like it doesn't recognize method generics if you have a Class Generic defined but not used.
Data data = new GenericClassFactory<ClassMatchable>().newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl");
That works fine. But it's dumb that I would have to define a class generic like that when it isn't needed for my purposes. I could do this:
public class GenericClassFactory {
public <E, K> E newObject(ClassMatcher<E, K> matcher, K key, String packageName){...}
public <T extends ClassMatchable, K> T newObject(K key, String packageName){...}
}
But now my second method seems like its too broad or something...maybe not. I mean it will still give a compile error if the object you are assigning to the return type doesn't implement ClassMatchable. Is that the way I should go? So that I don't have to typecast?
That's right, if you don't type a class reference, then even generic methods that only use method type parameters will not be generified. It's one of the weirder nuances of Java Generics. As you say, you can put in some arbitrary type for T:
Data data = new GenericClassFactory<ClassMatchable>().newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl");
But more likely this shouldn't even be an instance method. Can't it be a static method? If so you could just invoke it like this:
Data data = GenericClassFactory.newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl");
Edit
Note that this extends to all instance members, not just generic instance methods. Thus, there are simpler cases that demonstrate this odd nuance. This code compiles with only warnings:
public class Scratchpad<T> {
List<String> list;
public static void main(String[] args) {
Scratchpad sp = new Scratchpad();
List<Integer> list = sp.list;
}
}
And that's because sp.list is resolved as a List, not a List<String>, even though Scratchpad.list has nothing to do with T.
This is verbosely documented in the JLS, Section 4.8:
The type of a constructor (§8.8), instance method (§8.8, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the erasure of its type in the generic declaration corresponding to C. The type of a static member of a raw type C is the same as its type in the generic declaration corresponding to C.
You should tell the actual types of E and K when calling the method:
new GenericClassFactory<ClassMatchable>().<TypeforE, TypeforK>newObject(...)
It appears that Java can't infer it from the argument.
And, of course:
Its like it doesn't recognize method
generics if you have a Class Generic
defined but not used.
is exactly correct.
Its like it doesn't recognize method generics if you have a Class Generic defined but not used.
Exactly right. If you define a generic constraint on a class, and then instantiate the class without providing any generic constraint (that is, you leave off the <> completely), then you've just stepped into the realm of Raw Types, where nothing is the same anymore.
Raw Types only exist for backwards compatibility. According to Angelika Langer's excellent Java Generics FAQ,
The use of raw types in code written after the introduction of genericity into the Java programming language is discouraged. According to the Java Language Specification, it is possible that future versions of the Java programming language will disallow the use of raw types.
It also states:
Methods or constructors of a raw type have the signature that they would have after type erasure. A method or constructor call to a raw type generates an unchecked warning if the erasure changes the argument types.
If the newObject() method doesn't make use of the type parameter T of the class that it belongs to, then something is wrong with your design: most likely newObject() should be made a static method.
However, if it really must be an instance method for some reason, you may be able to get it to work by using the wildcard type GenericClassFactory<?>:
GenericClassFactory<?> gcf = new GenericClassFactory();
Data data = gcf.newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl");
Consider if T really is a type parameter of the class or the method. For example, is there something in the class that restricts created types to T (class-level), or perhaps it is being used as a convenience to avoid casting result values (method-level).
From what you've posted here, it seems to me that T should be a type on the method and that your last example is the answer. The method definition doesn't seem too wide, if the class implementation is generic and can give out different types with each method invocation.
I am trying to use a common technique to create objects from Xml. (Xml is legacy, so although there are already libraries to do this, it seemed faster to write this myself.)
I don't understand the compiler's complaint about the generic usage. Code sample:
public void createObjects() {
List<Object1> objectOnes = new ArrayList<Object1>();
List<Object2> objectTwos = new ArrayList<Object2>();
parseObjectsToList("XmlElement1", objectOnes);
parseObjectsToList("XmlElement2", objectTwos);
}
private void parseObjectsToList(String xmlTag, List<? extends Object> targetList) {
// read Xml and create object using reflection
Object newObj = createObjectFromXml(xmlTag);
targetList.add(newObj)
/* compiler complains: "The method add(capture#2-of ? extends Object) in the type List<capture#2-of ? extends Object> is not applicable for the arguments (Object)"
*/
/* If I change method signature to parseObjectsToList(String xmlTag, List targetList)
it works fine, but generates compiler warning about raw type */
}
Thanks for any enlightenment on the subject!
The problem you are running into is that, with the bounded wildcard that you have defined, you will be unable to add any element to the collection. From this tutorial:
List<? extends Shape > is an example of a bounded wildcard. The ? stands for an unknown type, just like the wildcards we saw earlier. However, in this case, we know that this unknown type is in fact a subtype of Shape. (Note: It could be Shape itself, or some subclass; it need not literally extend Shape.) We say that Shape is the upper bound of the wildcard.
There is, as usual, a price to be paid for the flexibility of using wildcards. That price is that it is now illegal to write into shapes in the body of the method
All a wildcard type means is that the actual type parameter T of the List that you pass as the second argument to parseObjectsToList is going to be a subtype of Object. It does NOT mean that the same List will be parameterized with different types.
So now you have a List<T> (called targetList) and you are trying to call targetList.add(Object). This is illegal because Object is not necessarily a subtype of T.
Because you are adding to the List rather than extracting elements from it, use List<Object> and make sure that's exactly what you pass in.
Using a List<Object> will work, but you might want keep your more precisely typed List<Object1> and List<Object2> for type-safety elsewhere. In that case, you'll need to check the type of each object before adding it to the List.
private void parseObjectsToList(String tag, List<T> list, Class<? extends T> c) {
// read Xml and create object using reflection
Object newObj = createObjectFromXml(tag);
list.add(c.cast(newObj)) ;
}
The cast() operation is a reflective equivalent to the static cast operator: (T) newObj
Using the altered method would look something like this:
parseObjectsToList("XmlElement1", objectOnes, Object1.class);
Think about what you are asking the compiler to do:
Given a list of "something that is a subtype of Object
Let me insert an Object into it
This doesn't make sense. Suppose your list is a list of Integer. Suppose that createObjectFromXml returns a String. It wouldn't make sense to allow inserting a String into a list typed for Integers.
So, your options are either to make your List a List<Object> or to find some way to make createObjectFromXml return a specific type, that you can then tie to the type of your list.