Java Class Generics and Method Generics conflicts - java

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.

Related

Get the return Type for inherited generic method

I have these 3 classes
public class Box<O> {
public O getItem() {...}
}
public class CoolBox extends Box<Integer> { ... }
public class AmazingBox extends CoolBox { ... }
At one point in my code, I need to get the return Type of the method getItem() for the AmazingBox class, but when accessing its methods via reflection, I get Object as the return Type instead of Integer.
Is there any way, (plain java or extra libraries) to get Integer as the return Type?
This is the code I used to get the return type:
Class<?> c = AmazingBox.class; // This is not how i get the class but its for demonstration purposes
Method m = c.getMethod("getItem");
Type t = m.getReturnType();
I just discovered that there is a library called GenericsResolver that does exactly what I want.
Using this piece of code it returns the correct type
Class<?> clazz = AmazingBox.class;
GenericsContext genericsContext = GenericsResolver.resolve(clazz);
Method method = clazz.getMethod("getItem");
Type methodReturnType = genericsContext.method(method).resolveReturnType();
While inheriting you can use generics for inheriting classes and create lower or upper bounds accordingly. If you don't do that assume you are working without using generics and won't get type safety on inheriting non-generic classes.
In short, if you go hybrid, type erasures will become effective with following rules (ref- oracle documentation)
Type Erasure
Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Insert type casts if necessary to preserve type safety.
Generate bridge methods to preserve polymorphism in extended generic types.
Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

Java name clash error, a method has the same erasure as another method

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

If the signature of a constructor includes the definition of a generic type, what extra responsibilities does that put on users of that constructor?

I extending an existing project that use generics in a few classes.
I am working on a class called PhiFunction, this class has one construcor which accepts a number of arguments. These include two arguments which I want to force to be the same type, E. These arguments are only used in the constructor, they are not stored as class fields.
I dont want to add this type to the class signature (I want to keep the class definition as simple as possible). The class signature just includes the type T at the moment. To keep the class signature as simple as possible, I added the definition of this type to the constructor signature, as shown below:
Simplified old code:
public class PhiFunction<T> {
...
public PhiFunction(
final MathematicalGroup<?> group, final List<?> baseElements, ...) {
Simplified new code:
public class PhiFunction<T> {
...
public <E extends GroupElement<T>> PhiFunction(
final MathematicalGroup<E> group, final List<E> baseElements, ...) {
The code compiles fine, and works fine.
The thing which surprises and confuses me now is that, it is still possible to create instances of PhiFunction as before. In other words, the addition of this type definition in the constructor signature does not change how the constructor is used (assuming that users do indeed supply arguments which use the same type). Users can still create instances of PhiFunction, just as they did before, without caring that this type definition was added to the constructor. I expected that users of this constructor would have to define E as being some particular class, but they dont.
I had not used generics in this way before. It seems that adding a type definition to the constructor (or to any method I guess) simply allows a type to be defined, without putting responsibilities on the user of the constructor to define that type.
I guess my question is, if the signature of a constructor includes the specification of a generic type, what extra responsibilities does that put on the user of the constructor?
I have one other related question. Should this type E be added to the Java docs as a parameter? and how? I am sure that it should be documented, as two of the arguments of the constructor must be of that type, but I am not sure how this should be added to the Javadocs.
You don't need to do any extra work for invoking a generic method or constructor. Most of the time, the compiler will be able to infer the type argument, based on the argument you pass to the method, or from the return type (not applicable here though).
So, when you create an instance of that class like this:
MathematicalGroup<Sometype> mathematicalGroup;
List<Sometype> list;
PhiFunction<Double> phiFunction = new PhiFunction<>(mathematicalGroup, list);
...the type parameter E of the constructor will automatically be inferred as Sometype.
However, if you pass arguments that doesn't comply to the rules of type parmeters, you'll see a compiler error, as in this case:
MathematicalGroup<Sometype> mathematicalGroup;
List<SomeOthertype> list;
// This will give a compiler error.
PhiFunction<Double> phiFunction = new PhiFunction<>(mathematicalGroup, list);
You also have the option to give explicit type arguments, if sometimes type inference doesn't work as expected:
// Redundant usage of explicit type argument
PhiFunction<Double> phiFunction =
new <Sometype>PhiFunction<Double>(mathematicalGroup, list);
Although the above usage of explicit type argument is redundant, but there are cases where compiler will not infer the type that you expect it to. Those are the cases when you pass inconsistent arguments to the parameters that are of same type parameter. Consider the below method for example:
public static <T> void fill( T [] array, T elem) {
for (int i=0; i<array.length; ++i) { array[i] = elem; }
}
.. if you try to invoke that method as:
fill(new String [5], new String ("XYZ")); // This is fine
fill(new String [5], new Integer (100)); // This is also fine? How?
Ideally you would expect the second method invocation to fail, because String and Integer should not be substitutable for same type parameter T. But here's the surprise. The compiler infers the type parameter as the intersection of all the super types of the arguments you passed. So, the type T is inferred as:
T:=Object&Serializable&Comparable
In that case, you might want to give <Object> as explicit type argument:
YourClass.<Object>fill(new String[5], new Integer(100));
P.S: Did you know that you can invoke non-generic methods in generic way? Well, generics is full of surprises :)
As for Javadoc, no you don't need to give any information about what E represent. Just explain what the paramters group and list means. That's it. The type parameter and formal parameters are anyways the part of the method signature, and will already be there. Consider for example Arrays.binarySearch() method.
To add it to Javadoc:
#param <E> This represents...
This constructor
public PhiFunction(
final MathematicalGroup<?> group, final List<?> baseElements, ...) {
Allows any type to be passed to/used with MathematicalGroup. Does MathematicalGroup have its own generics associated to it already?
If so, then perhaps your new constructor is redundant. That is, even though the original constructor has <?>, the generics restrictions of the MathematicalGroup class itself already does this enforcement.
public <E extends GroupElement<T>> PhiFunction(
final MathematicalGroup<E> group, final List<E> baseElements, ...) {
Otherwise, this is indeed more restrictive, as the original signature allows any type, and this one allows only GroupElement<T>, or one of its sub-classes.
As far as how your users use it:
MathematicalGroup mg = new <GroupElement<Object>>MathematicalGroup<Object>(groupElementInstance, ...);
This is an odd use-case. Usually this generics passing is done with static final utility functions.

Generic method vs wildcard - compilation error

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.

What does the generic nature of the class Class<T> mean? What is T?

I understand generics when it comes to collections. But what does it mean in the case of the Class<T> class? When you instantiate a Class object, there's only one object. So why the T parameter? What is it specifying? And why is it necessary (if it is)?
Type parameter <T> has been added to java.lang.Class to enable one specific idiom1 - use of Class objects as type-safe object factories. Essentially, the addition of <T> lets you instantiate classes in a type-safe manner, like this:
T instance = myClass.newInstance();
Type parameter <T> represents the class itself, enabling you to avoid unpleasant effects of type erasure by storing Class<T> in a generic class or passing it in as a parameter to a generic method. Note that T by itself would not be sufficient to complete this task2: the type of T is erased, so it becomes java.lang.Object under the hood.
Here is a classic example where <T> parameter of the class becomes important. In the example below, Java compiler is able to ensure type safety, letting you produce a typed collection from a SQL string and an instance of Class<T>. Note that the class is used as a factory, and that its type safety can be verified at compile time:
public static <T> Collection<T> select(Class<T> c, String sqlStatement) {
Collection<T> result = new ArrayList<T>();
/* run sql query using jdbc */
for ( /* iterate over jdbc results */ ) {
T item = c.newInstance();
/* use reflection and set all of item’s fields from sql results */
result.add(item);
}
return result;
}
Since Java erases the type parameter, making it a java.lang.Object or a class specified as the generic's upper bound, it is important to have access to the Class<T> object inside the select method. Since newInstance returns an object of type <T>, the compiler can perform type checking, eliminating a cast.
1 SUN Oracle has published a good article explaining all this.
2 This is different from implementations of generics without type erasure, such as one in .NET.
3 Java Generics tutorial by Oracle.
The answer by dasblinkenlight already demonstrated one of the main uses of this parameter. There is one more aspect I consider relevant: using that parameter, you can restrict the kind of class you want to pass at a given location. So e.g.
Class<? extends Number> cls
means that cls may be any class implementing the Number interface. This can help catching certain errors at compile time, and makes class argument requirements more explicit.
Perhaps a comparison to the case without generics is in order
// Java ≥5 with generics // Java <5 style without generics
Class<? extends Foo> c; Class c;
Foo t1 = c.newInstance(); Foo t1 = (Foo)c.newInstance();
Object obj; Object obj;
Foo t2 = c.cast(obj); Foo t2 = (Foo)c.cast(obj);
As you can see, not having T as an argument would require a number of explicit casts, as the corresponding methods would have to return Object instead of T. If Foo itself is a generic type argument, then all those casts would be unchecked, resulting in a sequence of compiler warnings. You can suppress them, but the core issue remains: the compiler cannot check the validity of these casts unless you properly use the type argument.
In Java there's a single metaclass: Class. Its instances (only one per type exists) are used to represent classes and interfaces, therefore the T in Class<T> refers to the type of the class or interface that the current instance of Class represents.
The use of generics in the Class type is top define the type of class. If I have ' Class obj' my object obj can holds only children of Charsequence.
This is an optional argument. I'm often put an '?' to avoid warnings from the Eclipse IDE if I don't need an specific type of class.

Categories