The following test renders fine in my IDE (Eclipse), but fails to compile when building via Maven.
The compiler error is shown in the comment line in the code block below.
It looks like the compiler is unable to determine the type of the 'o' input to the lambda. And if I cast o to the MyObj class, then it compiles fine.
I realize that this is a somewhat convoluted situation (we really do need this complexiy, though). And there really should be enough type info here for the compiler to determine the type (and the built-in compiler in Eclipse does so).
Am I doing something wrong with the generics declarations?
JDK is 21.0.5
public class AnotherTestClass {
public static class MyObj{
private final String arg1;
public MyObj(String arg1) {
this.arg1 = arg1;
}
public String getArg1() { return arg1; }
}
public static class MyFunctionHolder<T, R>{
Function<T, R> f;
public MyFunctionHolder(Function<T, R> f) {
this.f = f;
}
}
public static <C extends Collection<E>, E, R> MyFunctionHolder<C, R> forCollectionOfType(Class<? extends C> collectionClass, Class<E> elementClass, Function<C, R> function){
return new MyFunctionHolder<C, R>(function);
}
@Test
public void testMultiLevelStreams() {
List<MyObj> list = Arrays.asList(new MyObj("one"), new MyObj("two"));
MyFunctionHolder<List<MyObj>, List<String>> fh = forCollectionOfType( List.class,
MyObj.class,
l -> l.stream()
.map(o -> o.getArg1()) // compile error "cannot find symbol\n symbol: method getArg1()\n variable o of type java.lang.Object"
.toList()
);
List<String> rslt = fh.f.apply(list);
}
}
The objective is to have the static forCollectionOfType method accept the class of a collection, the class of the elements in the collection, and a function to apply to the collection itself.
One potentially useful data point is that when I do this, I get compile errors in Eclipse as well - so I'm really thinking I must be doing something wrong with the generics:
Function<List<MyObj>, List<String>> listFunction = l -> l.stream().map(o -> o.getArg1()).toList();
MyFunctionHolder<List<MyObj>, List<String>> fh2 = forCollectionOfType( List.class,
MyObj.class,
listFunction
);
Update
After reading this: Difference between <? super T> and <? extends T> in Java
I wound up making a small change to the generics in the method declaration:
Instead of Class<? extends C> collectionClass, I changed it to Class<? super C> collectionClass
The compiler error is now gone. However, I can now pass Object.class to the static method - and there is no compile time check to make sure the function generic parameters are consistent with the collection and element classes we pass in:
MyFunctionHolder<List<MyObj>, List<String>> fh = forCollectionOfType( Object.class, // Whoa - that's not a Collection!
MyObj.class,
l -> l.stream()
.map(o -> o.getArg1())
.toList()
It actually seems like I could remove the collection type entirely (the generic type checking is going to come from the function type).
However, we have downstream reasons for needing the collection class - but it looks like there is no way to ensure that the collection type is consistent with the function.
Let me know if I'm wrong or missing something!
C(here aList<MyObj>) and return anR(here, a String). But it looks like you're attempting to return aList<String>in your lambda expression. What is your intent with the function? I.e. what type does it take in, and what type does it return? Is it supposed to be aFunction<E, R>?Class<List>andClass<MyObj>parameters? Or just theClass<List>?Classas parameter just doesn't work. In other words: Mix generics andj.l.Classparameters? That's the root cause. Don't do that. Unfortunately, there is no single '.. just do this instead' answer. It depends on your API. Point is, you're heading in the wrong direction with this API. If you insist on keeping it, you're going to have to accept workarounds, casts that cause warnings, less compile time checking than you wanted, and wonky differences between ecj and javac inference engines.