Call generic method with class determined at runtime - java

I'm trying to automatically bind factory classes with a certain annotation using jersey 2/HK2. Therefore, I get the provided type at runtime from a generic interface and then try to bind the factory to this type. The method that binds the factory to a class looks like this:
protected void bindResourceFactory(Class<? extends Factory<?>> factory) {
Class<?> providedClass = getProvidedClass(factory);
bindFactory(factory).to(providedClass).in(Singleton.class);
}
The bindFactoy method provided by HK2 is defined as following:
public <T> ServiceBindingBuilder<T> bindFactory(Class<? extends Factory<T>> factoryType) {
return resetBuilder(AbstractBindingBuilder.<T>createFactoryBinder(factoryType, null));
}
This seems to work well when I build everything with eclipse. However when I build the project with maven, I get the following build error:
[ERROR] /Users/jan/Documents/Workspace/jersey-test/bind/ResourceFactoryBinder.java:[32,5] no suitable method found for bindFactory(java.lang.Class<capture#1 of ? extends org.glassfish.hk2.api.Factory<?>>)
[ERROR] method org.glassfish.hk2.utilities.binding.AbstractBinder.<T>bindFactory(java.lang.Class<? extends org.glassfish.hk2.api.Factory<T>>,java.lang.Class<? extends java.lang.annotation.Annotation>) is not applicable
[ERROR] (cannot infer type-variable(s) T
[ERROR] (actual and formal argument lists differ in length))
[ERROR] method org.glassfish.hk2.utilities.binding.AbstractBinder.<T>bindFactory(java.lang.Class<? extends org.glassfish.hk2.api.Factory<T>>) is not applicable
[ERROR] (cannot infer type-variable(s) T
[ERROR] (argument mismatch; java.lang.Class<capture#1 of ? extends org.glassfish.hk2.api.Factory<?>> cannot be converted to java.lang.Class<? extends org.glassfish.hk2.api.Factory<T>>))
[ERROR] method org.glassfish.hk2.utilities.binding.AbstractBinder.<T>bindFactory(org.glassfish.hk2.api.Factory<T>) is not applicable
[ERROR] (cannot infer type-variable(s) T
[ERROR] (argument mismatch; java.lang.Class<capture#1 of ? extends org.glassfish.hk2.api.Factory<?>> cannot be converted to org.glassfish.hk2.api.Factory<T>))
The java version in both cases is 1.8.0_152.
The reason probably is that my argument used is of type Class<? extends Factory<?>> whereas bindFactory expects Class<? extends Factory<T>>. Does someone know, why this might build with eclipse but not with maven? And is there any way to make this work apart from calling bindFactory via reflection?

This happens because eclipse uses it's own compiler named ECJ, and maven uses the javac compiler. Sometimes code that compiles in ECJ does not compile in javac and vice versa.
In this particular case the eclipse compiler is able to infer the generic type T but javac isn't. So you need to explicity tell the type T, which is unknown because the received type is Class<? extends Factory<?>>, this means you should use Object like the following.
this.<Object>bindFactory((Class<? extends Factory<Object>>) factory);
In this case factory needs to be casted, and this.<Object> can be ommited because the compiler already infers Object.
Finally you could suppress the cast warning, and it's better to 'uncheck' as little code as possible.
#SuppressWarnings({ "unchecked" })
Class<? extends Factory<Object>> objFactory = (Class<? extends Factory<Object>>) factory;
bindFactory(objFactory).to(providedClass).in(Singleton.class);
One important thing to consider is that the method getProvidedClass(...) should return the class correctly
Also the use of generics in the method like this <T> void bindResourceFactory(Class<? extends Factory<T>>) would take you to the same place again, because you wouldn't be able to call the method with a class extending Factory<?> with a wildcard (Class<? extends Factory<?>>).

The reason this error happens is because the compiler doesn't capture convert the "inner" wildcard in Class<? extends Factory<?>> (the one in Factory<?>). (In terms of the specification, "capture conversion is not applied recursively".)
It's easier to explain why this should happen with a different (but analogous with respect to the kind of types involved) example. Suppose we have a List of any type of List:
List<List<?>> lists = ...;
Now suppose we have some method that processes lists of lists, but assumes that the lists all have the same type:
<T> void process(List<List<T>> lists) {
// and at this point we should note that List<T>
// allows us to add elements to the lists, so we
// could do something like this:
if (!lists.isEmpty()) {
List<T> list0 = lists.get(0);
for (int i = 1; i < lists.size(); ++i)
list0.addAll(lists.get(i));
}
}
So the question is: should we be able to pass our List<List<?>> to the process method? Well, it could be that we've built our list of lists in something like the following way:
List<Double> doubles = new ArrayList<>();
Collections.addAll(doubles, 0.0, 1.0, 2.0);
List<String> strings = new ArrayList<>();
Collections.addAll(strings, "X", "Y", "Z");
List<List<?>> lists = new ArrayList<>();
Collections.addAll(lists, strings, doubles);
In that case it's more obvious that we shouldn't be able to pass the List<List<?>> to the process method taking a List<List<T>>. The way this is actually accomplished by the compiler is that it won't capture the "inner" wildcard to some type variable T.
The code in the question doesn't compile for a pretty similar reason. Since the type parameter on Class is mainly relevant to methods related to constructors (and in particular the newInstance method), we could show an example that's more similar using Supplier:
static void example(Supplier<? extends Factory<?>> s) {
capture(s);
}
static <T> void capture(Supplier<? extends Factory<T>> s) {
Factory<T> a = s.get();
Factory<T> b = s.get();
// remember, a and b are supposed to have the same type
T obj = a.provide();
b.dispose(obj);
}
The problem is that since our supplier could originally be a Supplier<Factory<?>>, there's no reason it couldn't, say, return a Factory<String> from one invocation and a Factory<Double> from another. We therefore shouldn't be able to capture Supplier<Factory<?>> to Supplier<Factory<T>>. Class.newInstance will always return objects of the exact same type, but the compiler doesn't know that.
I think that Eclipse's compiler is probably just wrong in this case to compile the code in the question.
If you want to force this to compile, you could use unchecked casts as that user in the comments is suggesting, but I don't know enough about the classes involved to say whether the result of that is actually provably correct. The above two code examples show how doing something like that could actually go horribly wrong (in principle), but Class is sometimes a special case.
A way to fix it that's more proper would be to declare a type variable on bindResourceFactory so it takes a Class<? extends Factory<T>> too, but I don't know if that actually works for the way you're calling the method.

Related

How can I use a generic class with wildcard declaration?

I have the following member in my class:
List<? extends SomeObject> list;
When I try to do:
list.add(list.get(0));
I get:
Test.java:7: error: no suitable method found for add(CAP#1)
list.add(list.get(0));
^
method Collection.add(CAP#2) is not applicable
(argument mismatch; Object cannot be converted to CAP#2)
method List.add(CAP#2) is not applicable
(argument mismatch; Object cannot be converted to CAP#2)
where CAP#1,CAP#2 are fresh type-variables:
CAP#1 extends Object from capture of ? extends Object
CAP#2 extends Object from capture of ? extends Object
My question is twofold:
Why doesn't it compile? Why can't I pass get()'s result to add()?
And how can I achieve this in another way without resorting to casting?
I understand that in a method with <T extends SomeObject> I can't just say:
T someObject = list.get(0);
list.add(someObject);
since my T could be another extension than the ? extension.
I also understand I can't say:
List<? extends SomeObject> list1;
List<? extends SomeObject> list2;
list1.add(list2.get(0));
But since the add and the get should work with the same generic type in list.add(list.get(0)) I don't understand why the compiler doesn't accept it.
What I really need is
[something of type T where T is whatever was used to instantiate list] someObject = list.get(0);
list.add(someObject);
so that I can later
list.add(someObject);
I don't think I should have to template my whole class to achieve this, should I?
class MyClass<T extends SomeObject> {
List<T> list;
and then later a method with
T someObject = list.get(0);
of course works, but screws other parts of my code.
So the first question is why doesn't this work, second question is what's the best workaround?
My question is twofold, why can't I do:
list.add(list.get(0));
Because the compiler isn't smart enough to know that you're adding something from list back into list. The compiler doesn't consider list.get(0) to have anything to do with list once it is evaluated: it's just "some expression" of type ? extends SomeObject.
To solve this, add a method with its own type variable:
private <T> void addFirst(List<T> list) {
list.add(list.get(0));
}
and replace the original list.add(list.get(0)); with an invocation of this:
addFirst(list);
This only defines a type variable on the method, and does not need to be visible outside the class, so you don't need a class-level type variable.
It's perhaps worth pointing out this is analogous to the Collections.swap method: that's using set rather than add, but, from a generics point of view, it's the same thing:
#SuppressWarnings({"rawtypes", "unchecked"})
public static void swap(List<?> list, int i, int j) {
// instead of using a raw type here, it's possible to capture
// the wildcard but it will require a call to a supplementary
// private method
final List l = list;
l.set(i, l.set(j, l.get(i)));
}
This takes an approach which is technically type-safe, and does avoid casts; but it's a bit gross, because it uses raw types.
I would imagine that it is only like this for backwards-compatibility reasons. Given a chance to write it again, you could just define a type variable as in the addFirst method above.
When use wildcards, we should follow The Get and Put Principle which introduced in Java Generics and Collections:
The Get and Put Principle: use an extends wildcard when you only get values out of a
structure, use a super wildcard when you only put values into a structure, and don’t use
a wildcard when you both get and put.
In your case, don't use a wildcard as you both get element from list and put element to list.

Method is not applicable for arguments when adding wildcard

I have following two classes:
class ProblematicConverter implements Converter<List<?>> {};
class NonProblematicConverter implements Converter<List> {};
And method of class Utils:
<T> void addConverter(Class<? extends T> cls, Converter<T> converter);
Now, first function invocation is ok but second produces error:
addConverter(List.class, new ProblematicConverter());
addConverter(List.class, new NonProblematicConverter());
Error says:
"The method addConverter(Class, Converter) in the type Utils is not applicable for the arguments (Class, ProblematicConverter)"
I do not understand why it's like that.
Based on your comment, I think the right thing to do here might be to use an unchecked cast on List.class, but first, the reason the code in the question doesn't compile is roughly:
T of addConverter is inferred to be List<?>.
The bounded wildcard in cls requires that its type argument be T or a subtype of T, but the raw type List is a supertype of List<?> (specified here).
Therefore, Class<List> is incompatible with the inferred type of cls which is Class<? extends List<?>>.
So, for example, either of the following two declarations would compile with the invocations in the question:
<T> void m(Class<T> cls, Converter<? extends T> converter) {}
<T> void m(Class<? super T> cls, Converter<T> converter) {}
That of course doesn't help you out, but it illustrates the relationship between List and List<?>.
You might see also these two answers of mine which discuss similar situations.
So anyway, based on your comment saying that you're trying to eliminate raw types and can't change the declaration of addConverter, what could be appropriate is to use an unchecked cast from Class<List> to Class<List<?>>:
#SuppressWarnings("unchecked")
static final Class<List<?>> WILD_LIST =
(Class<List<?>>) (Class<? super List<?>>) List.class;
This will let you call e.g.:
addConverter(WILD_LIST, new ProblematicConverter());
However, I'd like to point out that unchecked casting is not a general solution. It's a solution to this specific problem of converting e.g. a Class<GenericType> to a Class<GenericType<?>>. It's safe to do because GenericType<?> is a more restrictive type than the raw type GenericType and because Class has a very limited range of things it can do with its type argument.
If you could change the declaration of addConverter, I think I would recommend using something like Guava TypeToken instead of Class, because then you don't have this sort of problem.

Java 8 type inference cause to omit generic type on invocation

I have a Problem with a generic method after upgrading to Java 1.8, which was fine with Java 1.6 and 1.7
Consider the following code:
public class ExtraSortList<E> extends ArrayList<E> {
ExtraSortList(E... elements) {
super(Arrays.asList(elements));
}
public List<E> sortedCopy(Comparator<? super E> c) {
List<E> sorted = new ArrayList<E>(this);
Collections.sort(sorted, c);
return sorted;
}
public static void main(String[] args) {
ExtraSortList<String> stringList = new ExtraSortList<>("foo", "bar");
Comparator<? super String> compGen = null;
String firstGen = stringList.sortedCopy(compGen).get(0); // works fine
Comparator compRaw = null;
String firstRaw = stringList.sortedCopy(compRaw).get(0); // compiler ERROR: Type mismatch: cannot convert from Object to String
}
}
I tried this with the Oracle javac (1.8.0_92) and with Eclipse JDT (4.6.1) compiler. It is the same result for both. (the error message is a bit different, but essentially the same)
Beside the fact, that it is possible to prevent the error by avoiding raw types, it puzzles me, because i don't understand the reason.
Why does the raw method parameter of the sortedCopy-Method have any effect on the generic type of the return value? The generic type is already defined at class level. The method does not define a seperate generic type. The reference list is typed to <String>, so should the returned List.
Why does Java 8 discard the generic type from the class on the return value?
EDIT: If the method signature of sortedCopy is changed (as pointed out by biziclop) to
public List<E> sortedCopy(Comparator c) {
then the compiler does consider the generic type E from the type ExtraSortList<E> and no error appears. But now the parameter c is a raw type and thus the compiler cannot validate the generic type of the provided Comparator.
EDIT: I did some review of the Java Language Specification and now i think about, whether i have a lack of understanding or this is a flaw in the compiler. Because:
Scope of a Declaration of the generic type E is the class ExtraSortList, this includes the method sortedCopy.
The method sortedCopy itself does not declare a generic type variable, it just refers to the type variable E from the class scope. see Generic Methods in the JLS
The JLS also states in the same section
Type arguments may not need to be provided explicitly when a generic method is invoked, as they can often be inferred (§18 (Type Inference)).
The reference stringList is defined with String, thus the compiler does not need to infer a type forE on the invocation of sortedCopy because it is already defined.
Because stringList already has a reified type for E, the parameter c should be Comparator<? super String> for the given invocation.
The return type should also use the already reified type E, thus it should be List<String>.
This is my current understanding of how i think the Java compiler should evaluate the invocation. If i am wrong, an explanation why my assumptions are wrong would be nice.
To bring an final answer to why this happens:
Like #Jesper mentioned already, you're using raw types when you shouldn't (Especially when using the Generic as type in multiple cases).
Since you pass an Comparator without an Generic-Type, there will actually be none. You could think of the E-Generic as null to make it easier. Therefore your code becomes to this:
public List sortedCopy(Comparator c) {
List sorted = new ArrayList(this);
Collections.sort(sorted, c);
return sorted;
}
Now you're attemptig/assuming you get an String from an List without Generics and therefore an Object (hence it's the super-class of everything ).
To the question why the raw-type parameter has no effect on the return type, since you don't specify an certain Level of abstraction. You'd have to define an Type that the Generic has to extend/implement at least to make that happen (compilation errors), for example.
public class ExtraSortList<E extends String> extends ArrayList<E> {
will now only allow Strings or Classes which extend it (not possible here since string is final). With that, your fallback Type would be String.

Generic type incompatibility

I have an issue with generics types on class using JOOQ.
public abstract class BaseDataAccessObject<T extends BaseDataClass, U extends UpdatableRecord> {
protected abstract RecordMapper<U, T> getRecordMapper();
public T insert(T data) throws Exception{
//Some code ...
U record = getRecord(data);
record.store();
return record.map(getRecordMapper()); // <-- PROBLEM HERE !
}
}
Map accept one parameter of this type RecordMapper<Record, E> and it return an object of type E.
I face this issue :
Error:(110, 22) java: method map in interface org.jooq.Record cannot be applied to given types;
required : org.jooq.RecordMapper<org.jooq.Record,E>
found : org.jooq.RecordMapper<U,T>
reason: cannot infer type-variable(s) E argument mismatch; org.jooq.RecordMapper<U,T> cannot be converted to org.jooq.RecordMapper<org.jooq.Record,E>)
I don't understand why because :
Generic U, inherits form UpdatableRecord who inherits from org.jooq.Record, then Ushould be compatible with org.jooq.Record.
E should be compatible with T (no ancestor is defined).
I have to keep the U extending UpdatableRecord.
This Record.map(RecordMapper) method was a mistake from early days in jOOQ. It makes absolutely no sense at all to have a map method on a non-monadic type, i.e. on a non-wrapper type. E.g. Stream.map() is perfectly fine, because a stream can map its contents to something else, producing another stream. Optional.map() is perfectly fine, because an optional can map its contents to something else, producing another optional.
But an item / value shouldn't be able to map itself. There would have been a slight possibility to rectify this by using recursive generics, but that would have been an even bigger mistake.
But luckily, here's the solution to your problem, and it's really easy:
U record = getRecord(data);
record.store();
return getRecordMapper().map(record); // <-- PROBLEM HERE NO MORE !

Get generic type of method

Maybe this have been answered before, but I did not find it here. Consider the following line of code:
public static <T> T getAs() { ... }
It is possible to obtain the object Class<T>, by reflection ?
For clarification, im not looking the generic type of a class, like in
class Foo<T> {
public T get() { ...}
}
This can be done in c# at it is really handy, for example
class Foo {
private List<A> items;
public <T extends A> T first() {
Class<T> clazz = //Magic??
foreach (A a : items) {
if (clazz.isInstance(a)) return (T)a;
}
return null;
}
}
Used like this:
SubClass s = fooInst.<SubClass>first();
Sometimes, it is indeed possible to get hold of generic type information even after compilation. In theory, this would even be possible for generic method calls. Spoiler, this is a bad idea. For academic reasons, let's however consider how this would be possible:
Check the execution stack from within the method.
Extract the calling class.
Locate and interpret the calling class's class file and parse its calling method's byte code.
Find the line where the method is called from.
Emulate javac's type inference mechanism and locate the same type for T that javac would have infered.
Load the class by its name you found for T.
This will of course not work very well and it would be broken if a type for T was specified explicitly in the source code. And you would not want to do this as your method call would suddenly require IO.
What you probably want is to return :
<T> T getAs(Class<? extends T> type) { ... }
In this case, you require the user to supply you with the required type but you do it in a type-safe manner.

Categories

Resources