I'm trying to use generic methods in java for the first time. It isn't working, and it appears to me that Java generics are simply not meant to be used this way - that I will need to implement a common interface rather then using a generic method. Please confirm (or deny) that conclusion.
This is what I'm trying to do:
public <T> void updateItem( T item ) {
String itemKey = item.getKey();
...
This gives me the error 'The method getKey() is undefined for the type T'.
Looking into why this doesn't work I see that type erasure "replaces all type parameters in generic types with their bounds or Object if the type parameters are unbounded".
The only way I can 'bound' my type T is if I create a common subclass or interface for all the types I was planning to use, and if I do that then I can use polymorphism rather then generics.
This is right. Method getKey() is not defined in class T. However if you define your method as following:
public <T extends Entry> void updateItem( T item ) {
String itemKey = item.getKey();
the compiler can find getKey() method. Please pay attention that I used <T extends Entry> (I mean java.util.Map.Entry). This class has method getKey() and compiler can resolve it.
Related
How to call a Custom Generic method by reflection in java?
class Person
{
public <T> void print(T t)
{
System.out.println(t.toString());
}
}
Generics are erased at compile time, they only provide extra information to the compiler to determine errors. They do not actually change the signature of the method in the .class file.
This means that you call a generic method by reflection in Java exactly the same way as you would call a non-generic method in Java, except that instead of specifying a type of T, you would specify a type of Object.
There are so many tutorials on how to call a regular method by reflection that I hesitate to add yet another; however, if you really need direction on how to call a method by reflection, please add a comment below and I'll add the necessary code.
If you find that things are not working as expected, you can always run javap on the compiled class file to verify that you are using the right objects in the argument list. It might be possible that if you specify a <T extends List> type generic signature, the resulting parameter object might actually be a List object.
This works for me.
Method method = Person.class.getMethod("print", Object.class);
method.invoke(new Person(), "this is a string");
method.invoke(new Person(), 273);
method.invoke(new Person(), new Object());
printing
this is a string
273
java.lang.Object#addbf1
Of course the theory behind this is explained beautifully in #Edwin's answer.
To highlight the point given in Edwin's answer, where we are using extends in a generic type: if you have a class like
GenericHibernateDao<T extends Serializable>
, and a method
public T save( T entity ) {};
to invoke the method save using reflection you have to use the Serializable class, i.e., you need to use:
Method method = GenericHibernateDao.class.getMethod(methodName, Serializable.class);
and not the Object.class as the parameter, since we are using
<T extends Serializable>
I have two classes as follows
class QueryResult:
public class QueryResult
{
...
public static List sortResults(boolean ascending, List<QueryResult> toSort)
{ ...
}
}
and class CaseResult:
public class CaseResult extends QueryResult
{
...
public static List sortResults(boolean ascending, List<CaseResult> toSort)
{ ...
}
}
I am getting the following error:
Name clash: The method sortResults(boolean, List) of type
CaseResult has the same erasure as sortResults(boolean,
List) of type QueryResult but does not hide
it CaseResult.java
I tried answers under the similar question Java generics name clash , has the same erasure but got more errors. I think I may misunderstand something or those answers do not fit my case.
Could someone please provide any solutions or explain more to help me understand? Thank you all.
The reason for the error is covered in Darshit Chokshi's answer.
However, since a solution/alternative has not been posted yet, try the following:
You are trying to override the sortResults() method in such a way so that you can sort lists with different elements. However, when overriding you need to have the same type signature. The signature looks similar in your case, but since the elements in the List differ - the compiler realises List< QueryResult> and List< CaseResult> is different, but due to type erasure in Java, it would be uncertain which method it should be calling - i.e. should it call the super class method or the subclass method.
Instead of overriding, rather change the original method in the super class (in your case, QueryResult) so that it can handle anytype of List element. You can use wildcard capturing to accomplish this:
public class QueryResult {
...
public <T> static List<T> sortResults(boolean ascending, List<T> toSort) {
...
}
}
This method receives a list and will infer the element type, assigning the element type to the generic type parameter T. Whenever you then want to refer to the element type in the body of the method, instead of using the element type name (previously either QueryResult or CaseResult), now you would use the generic type variable T instead.
You can also put further bounds on this variable if required (i.e. if the list element needs to be a subtype of QueryResult you can say < T extends QueryResult>), this will force Java to do a type check on the List and what type of elements it may have.
A further comment on your original code. Be very careful of using raw types in new generic code - you are returning a list as a raw type: List, without specifying what the element type of the list is i.e. a parameterised type: List< SomeActualType>. This can cause many problems and is not advised. The Java creators kept this form of coding in Java for backward compatibility of code that existed before generics, but it is strongly advised not to code in this manner for new written code.
You can read up more about the raw type vs parameterised type and the pitfalls of using raw types, as well as more information on wildcard capturing and bounds, in the textbook:
Effective Java, by Joshua Bloch
Section: Generics
Item 23: Don't use raw types in new code
Item 28: Use bounded wildcards to increase API flexibility
As per Java Documentation,
If a subclass defines a static method with the same signature as a static method in the superclass, then the method in the subclass hides the one in the superclass.
The distinction between hiding a static method and overriding an instance method has important implications:
The version of the overridden instance method that gets invoked is the one in the subclass.
The version of the hidden static method that gets invoked depends on whether it is invoked from the superclass or the subclass
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 need to obtain (as a String) the type signatures of certain Java types.
For example, this is the type signature of certain ParameterizedType:
Lorg/mapackage/MyClass<Ljava/lang/String;>;
I need this in the context of an application being developed with Javassist.
To explain better what I am looking for, I show an extract of a method createGetter that uses Javassist to generate a getter method for a private field. The type of the field is a parameterized type (i.e., it uses generics).
The createGetter method receives as a first parameter the type of the field, the second parameter is the name of the field, and the third is the class where the method should be added:
public CtMethod createGetter(Type propertyType, String propertyName, CtClass ctDeclaringClass) {
CtMethod ctGetterMethod = ...
if(propertyType instanceof ParameterizedType //parameterized "single" type
|| propertyType instanceof GenericArrayType) { //parameterized array
String signature = asGenericSignature(propertyType); //MISSING PIECE OF THE PUZZLE !
ctGetterMethod.setGenericSignature(signature);
}
return ctGetterMethod;
}
For example, if I have a class
public class TestClass{
private MyClass<String> myField;
}
Then after calling the createGetter method for the field myField, the class becomes:
public class TestClass{
private MyClass<String> myField;
public MyClass<String> getMyField() {
return MyClass<String> myField;
}
}
The return type of the generated getter method should have the same generic signature as the field (the code could compile without the method having the same generic signature of the field since having just the same class is fine for the compiler. The reasons I need it to have the same type generic signature are not explained here).
The code of the createGetter method illustrates that with Javassist, in order to set the generic type signature of a method, I need first to obtain such a type signature as a String (if there is another way please someone tell me).
I know the exact parameterized type the getter method should have since it is exactly the same than the field. But I have such type as an instance of ParameterizedType.
My question is: how can I obtain these type signatures as strings (including type parameters data) given any arbitrary Java Type ?.
Thanks for any help.
Eclipse internally uses descriptors/signatures for Java Source Code. These also include Generic information, which might be useful for you. The Interface IMethod might be a good starting point for investigation.
How to call a Custom Generic method by reflection in java?
class Person
{
public <T> void print(T t)
{
System.out.println(t.toString());
}
}
Generics are erased at compile time, they only provide extra information to the compiler to determine errors. They do not actually change the signature of the method in the .class file.
This means that you call a generic method by reflection in Java exactly the same way as you would call a non-generic method in Java, except that instead of specifying a type of T, you would specify a type of Object.
There are so many tutorials on how to call a regular method by reflection that I hesitate to add yet another; however, if you really need direction on how to call a method by reflection, please add a comment below and I'll add the necessary code.
If you find that things are not working as expected, you can always run javap on the compiled class file to verify that you are using the right objects in the argument list. It might be possible that if you specify a <T extends List> type generic signature, the resulting parameter object might actually be a List object.
This works for me.
Method method = Person.class.getMethod("print", Object.class);
method.invoke(new Person(), "this is a string");
method.invoke(new Person(), 273);
method.invoke(new Person(), new Object());
printing
this is a string
273
java.lang.Object#addbf1
Of course the theory behind this is explained beautifully in #Edwin's answer.
To highlight the point given in Edwin's answer, where we are using extends in a generic type: if you have a class like
GenericHibernateDao<T extends Serializable>
, and a method
public T save( T entity ) {};
to invoke the method save using reflection you have to use the Serializable class, i.e., you need to use:
Method method = GenericHibernateDao.class.getMethod(methodName, Serializable.class);
and not the Object.class as the parameter, since we are using
<T extends Serializable>