Why doesn't this Java 8 stream example compile? - java

I'm trying to figure out why this code does not compile on JDK 1.8.0_45:
public class Example<E extends Example<E>> {
public List<? extends Example<?>> toExamples(Collection<String> collection) {
return collection.stream()
.map(v -> lookup(v))
.collect(Collectors.toList());
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
Adding a seemingly unnecessary cast fixes it:
public class Example<E extends Example<E>> {
public List<? extends Example<?>> toExamples(Collection<String> collection) {
return collection.stream()
.map(v -> (Example<?>) lookup(v))
.collect(Collectors.toList());
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
Here's the error from the compiler:
Example.java:9: error: incompatible types: inference variable R has incompatible bounds
.collect(Collectors.toList());
^
equality constraints: List<Object>
upper bounds: List<? extends Example<?>>,Object
where R,A,T are type-variables:
R extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
A extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
T extends Object declared in interface Stream
For some reason, the return type of lookup() isn't correctly inferred to something extending Example.

As Peter Lawrey pointed out, ? extends Example<?> is not compatible with E extends Example<E>. Still, even fixing the signature doesn’t make type inference work here.
The reason is a known limitation of the type inference as it does not back-propagate through chained method invocations. In other words, the return type allows to infer the types for the collect(…) invocation but not for the preceding map(…) invocation. (see also this answer)
But it works for nested method invocations, so the following rewritten method can be compiled:
public class Example<E extends Example<E>> {
public <E extends Example<E>> List<E> toExamples(Collection<String> collection) {
return collection.stream()
.collect(Collectors.mapping(v -> lookup(v), Collectors.toList()));
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
Still, you have to rethink the semantics of your code. A method’s type parameter which appears only at the return type can’t be correct as it implies that “whatever the caller substitutes for this type parameter, the method will return the right thing”. Since the method implementation doesn’t know what the caller assumes, this is impossible. Only returning null or an empty list will work correctly, which is of little use.

When you have a ? it doesn't equal another ? i.e. the compiler doesn't see
? extends Example<?>
as a match for
E extends Example<E>
as it cannot assume the two ? are the same. It could be
A extends Example<B>
When you perform the cast, you obscure the constraint so it can match.

My guess is that the generic type defined in the static method is not the same as the generic type defined in the class. You should be able to make the lookup method non-static so it matches the same type defined in the class level generic declaration:
public E lookup(String value) {
return null;
}

Related

Java method with two interdependent generic type constraints

Not sure how to put it in words, so I'll just start with the code I currently have:
// "root type" for all resources
// fixed
public class ResourceClassA
{ }
// "base type" for all resources for a specific domain
// fixed
public class ResourceClassB extends ResourceClassA
{ }
// specific resource type
// can be derived from further but don't think that matters here
// not fixed but heavily constrained in other ways
public class ResourceClassC extends ResourceClassB
{ }
// only needed for a negative example below, irrelevant otherwise
public class ResourceClassD extends ResourceClassB
{ }
// fixed
public class Remote
{
public <T extends ResourceClassA> Set<T> read(Class<T> baseType, Class<? extends T> subType);
}
// semi-fixed: read() signature can be modified
public interface AbstractRemoteAccess<T extends ResourceClassA>
{
Set<T> read(Class<? extends T> clazz);
}
// semi-fixed: read() signature can be modified
public class SpecificRemoteAccess<T extends ResourceClassA> implements AbstractRemoteAccess<T>
{
private Class<T> _baseType;
private Remote _remote;
public Set<T> read(Class<? extends T> clazz)
{
return _remote.read(_baseType, clazz);
}
}
// not fixed
public class ConsumerClass
{
public void doSomething(AbstractRemoteAccess<ResourceClassB> remoteAccess)
{
Set<ResourceClassB> rawObjects = remoteAccess.read(ResourceClassC.class);
Set<ResourceClassC> castedObjects = rawObjects.stream()
.map(c -> (ResourceClassC) c)
.collect(Collectors.toSet());
}
}
All classes marked with fixed cannot be changed, they are provided as is - vice versa for not fixed. Class SpecificRemoteAccess is the one I'm looking to change: I would like the read() method to
not return its result as Set but as a Set<> of generic type matching clazz
so that the caller does not have to cast the method's result, see ConsumerClass.doSomething()
and all of this without loosing type-safety
The easiest way I saw was to do
Set<V> read(Class<V extends T> clazz)
but that produces this error:
Incorrect number of arguments for type Class<T>; it cannot be parameterized with arguments <V, T>
which, if I'm interpreting it correctly, means the compiler is treating V & T as separate type arguments for Class which doesn't match its definition.
Next I tried adding a second generic type V and using it as generic type for the return type of read(). I started with
<V extends T> Set<V> read(Class<? extends T> clazz)
which doesn't constrain V to clazz at all, meaning both of these will be accepted by the compiler
Set<ResourceClassC> correct = remoteAccess.read(ResourceClassC.class);
Set<ResourceClassD> incorrect = remoteAccess.read(ResourceClassC.class);
The type declaration for incorrect is semantically wrong but syntactically fine. So I tried to constrain V based on clazz but the only solution I could think of is
<V extends T> Set<V> read(Class<V extends T> clazz, Class<V> classV)
which does, somewhat, fix the problem from above:
// compiles
Set<ResourceClassC> correct = remoteAccess.read(ResourceClassC.class,
ResourceClassC.class);
// error: Type mismatch: cannot convert from Set<ResourceClassC> to Set<ResourceClassD>
Set<ResourceClassD> incorrect = remoteAccess.read(ResourceClassC.class,
ResourceClassC.class);
but not only does it make the read() call cumbersome (users will be wondering why they have to pass the same info twice) but also error prone:
// compiles
Set<ResourceClassC> correct = remoteAccess.read(ResourceClassC.class,
ResourceClassC.class);
// type error
Set<ResourceClassD> incorrect = remoteAccess.read(ResourceClassC.class,
ResourceClassC.class);
// compiles but will cause run-time cast failures
Set<ResourceClassD> incorrect2 = remoteAccess.readAndCast2(ResourceClassC.class,
ResourceClassD.class);
Given consumer-side developers are faced with hundreds of resource classes like ResourceClassC, making read() error prone simply is no option.
Would appreciate if someone could point out my mistake.

Java generics - cast assignable capture type to subclass

I have the following scenario in Java generics:
public abstract class A<T> {
protected final Class<T> typeOfX;
public A(final Class<T> typeOfX) {
this.typeOfX = typeOfX;
}
public abstract void load(final T x);
}
public class AnyA<S> extends A<S> {
private final Map<String, A<? extends S>> map;
public AnyA(final Class<S> superTypeOfX,
final Map<String, A<? extends S>> map) {
super(superTypeOfX);
this.map = map;
}
#Override
public void load(final S superx) {
for (final A<? extends S> a: map.values())
if (a.typeOfX.isAssignableFrom(superx.getClass())) //Here I want to say: "if superx can be casted to a.typeOfX".
a.load(a.typeOfX.cast(superx)); //Here I want to cast superx to a.typeOfX (so as to call the load method). Here's the compile error.
}
}
I'm getting the error:
incompatible types: S cannot be converted to CAP#1
where S is a type-variable:
S extends Object declared in class AnyA
where CAP#1 is a fresh type-variable:
CAP#1 extends S from capture of ? extends S
AnyA is a composite A, i.e. is an A which maintains several other A instances.
AnyA in its load(...) method shall decide which of the maintained A instances should be used to "pass the loading process to" of the argument.
In other words, AnyA is responsible for finding the correct A to load the argument.
But also AnyA is an A because it handles loading the argument.
My question is:
Why is this cast not possible, by the time I know that S is a sub-class of T and all A instances in AnyA can load a subclass of S?
How can I overcome this problem without changing the class diagram too much?
I have read about "helper methods" but cannot match the example shown there to my problem.
I'm using NetBeans IDE with Java SDK 8.
Note that regardless of what you do, the code is not "syntactically type safe" in any case. There is an unchecked cast, and the only safety belt that prevents this from going wrong is the isAssignableFrom check.
(That is often OK, I'm just mentioning it for completeness)
The reason for the error may be more obvious when you pull the lines apart (here, S stands for SuperType, according to the Type Parameter Naming Conventions - please follow them!)
A<? extends S> a = ...;
S s = a.typeOfX.cast(s);
a.load(s);
The A<? extends S> intuitively means that it is an A that can accept an unknown type in its load method. You know that it extends type S, but you do not know which type this is.
It may become blatantly obvious when you insert Object for S:
A<String> specificA = ...;
// So the "specificA" can load "String" objects. Then this is fine:
A<? extends Object> a = specificA;
Object s = a.typeOfX.cast(s);
// But here's the error: "s" is only an Object, and not a String!
a.load(s);
I think the main point of confusion (and the main reason for the question) was the following: When calling
Object s = a.typeOfX.cast(s);
and typeOfX is String.class, then the return type of the cast will not be String, but only the type that the compiler can infer at this point. And this is Object, in the example above.
However, you already referred to the Helper Methods, and indeed, with some trickery, you can make this compile,
but... (see notes below)
import java.util.Map;
abstract class A<T>
{
protected final Class<T> typeOfX;
public A(Class<T> typeOfX)
{
this.typeOfX = typeOfX;
}
public abstract void load(T x);
}
class AnyA<S> extends A<S>
{
private final Map<String, A<? extends S>> map;
public AnyA(Class<S> superTypeOfX,
Map<String, A<? extends S>> map)
{
super(superTypeOfX);
this.map = map;
}
#Override
public void load(S s)
{
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(s.getClass()))
{
callLoad(a, s);
}
}
}
private static <S, T extends S> T cast(A<T> a, S s)
{
T t = a.typeOfX.cast(s);
return t;
}
private static <T, S extends T> void callLoad(A<S> a, T s)
{
a.load(cast(a, s));
}
}
I would not recommend this in practice.
Personally and subjectively: I think that when you are doing the isAssignableFrom check, then the (unchecked) cast should be as close as possible to this check. Otherwise, the code will be very hard to understand.
So although unchecked casts are a code smell in practice, and I try to avoid SuppressWarning whenever possible, I would consider this as far more readable:
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(superx.getClass()))
{
// This call is safe as of the check done above:
#SuppressWarnings("unchecked")
A<Object> castA = (A<Object>) a;
castA.load(superx);
}
}

Incompatible classes of java generics

It seems I'm stuck with java generics again. Here is what I have:
Couple of classes:
class CoolIndex implements EntityIndex<CoolEntity>
class CoolEntity extends BaseEntity
Enum using classes above:
enum Entities {
COOL_ENTITY {
#Override
public <E extends BaseEntity, I extends EntityIndex<E>> Class<I> getIndexCls() {
return CoolIndex.class;
}
#Override
public <E extends BaseEntity> Class<E> getEntityCls() {
return CoolEntity.class;
}
}
public abstract <E extends BaseEntity, I extends EntityIndex<E>> Class<I> getIndexCls();
public abstract <E extends BaseEntity> Class<E> getEntityCls();
}
Function I need to call with use of result of getIndexCls() function call:
static <E extends BaseEntity, I extends EntityIndex<E>> boolean isSomeIndexViewable(Class<I> cls)
The problem is that compiler complains about return CoolIndex.class; and return CoolEntity.class; and it's not clear to me why... Of course I can cast it to Class<I> (first case) but it seems to me like I'm trying to mask my misunderstanding and it doesn't feel right.
The problem with getIndexCls is that because it's generic, the type parameters can be interpreted to be any classes that fit the bounds on the declarations. You may think that CoolIndex.class fits those bounds, and it does, but a caller of the method can supply their own type arguments which would be incompatible, e.g.:
Entities.COOL_ENTITY.<UncoolEntity, UncoolIndex>getIndexCls();
That would break type safety, so the compiler disallows this. You can cast to Class<I>, but the compiler will warn you about an unchecked cast for the same reason. It will compile, but it can cause runtime problems as I've described.
Other situations can resolve such a situation by passing a Class<I> object to make the type inference work properly, but that defeats the point of this method -- returning a Class<I> object.
Other situations call for moving the generic type parameters from the method to the class, but you are using enums, which can't be generic.
The only way I've come up with to get something similar to compile is by removing the enum entirely. Use an abstract class so you can declare class-level type parameters. Instantiate constants with the type arguments you desire.
abstract class Entities<E extends BaseEntity, I extends EntityIndex<E>> {
public static final Entities<CoolEntity, CoolIndex> COOL_ENTITY = new Entities<CoolEntity, CoolIndex>() {
#Override
public Class<CoolIndex> getIndexCls() {
return CoolIndex.class;
}
#Override
public Class<CoolEntity> getEntityCls() {
return CoolEntity.class;
}
};
// Don't instantiate outside this class!
private Entities() {}
public abstract Class<I> getIndexCls();
public abstract Class<E> getEntityCls();
}
This can be reproduced by much simpler example:
public <E extends BaseEntity> E get() {
return new BaseEntity(); // compilation error here
}
The problem in such declaration <E extends BaseEntity> is that your method claims to return an instance of any type E that caller should ask:
MyCoolEntity1 e = get(); // valid, E is MyCoolEntity1
MyCoolEntity2 e = get(); // valid, E is MyCoolEntity2
This code should be compile-time safe, so you have to cast result of your method to E
public <E extends BaseEntity> E get() {
return (E) new BaseEntity(); // no error, but unsafe warning
}
In your example it's pretty the same, you claim to return value of type Class<E>:
public <E extends BaseEntity> Class<E> getEntityCls()
But return a concrete class SomeEntity.class which is Class<CoolEntity>
OK, how should I fix that?
You can add type cast return (Class<I>) CoolIndex.class; / return (Class<E>) CoolEntity.class;
You can replace enum with classes, since enums can not be generic and classes can
You can entirely remove generics, since there's no much value in it

Can the return type of a static generic method depend on its arguments?

I "simply" want to write a static generic method that takes the generic Collection<E> of any type E as its input, and outputs a result of the appropriate type Vector<E>. Since the type E is always known at compile-time, this should not be a problem - but it is... Thus, a call should later look like:
Collection<String> coll = ...
Vector<String> vec = Convert.toVector(coll); // either this or...
Vector<String> vec = Convert<String>.toVector(coll);
Here is what I tried - all without success:
import java.util.Collection;
import java.util.Vector;
public class Convert<E> {
// 1st try, same type E as class => Error: Cannot make a static reference to the non-static type E
public static Vector<E> toVector1(Collection<E> coll) {
return new Vector<E>();
}
// 2nd try, a new type X. => Error: X cannot be resolved to a type
public static Vector<X> toVector2(Collection<X> coll) {
return new Vector<X>();
}
// 3rd try, using wildcard. => Error: Cannot instantiate the type Vector<?>
public static Vector<?> toVector3(Collection<?> coll) {
return new Vector<?>();
}
// 4th try, using bounded wildcard. => Error: Cannot make a static reference to the non-static type E
public static Vector<? extends E> toVector4(Collection<? extends E> coll) {
return new Vector<E>();
}
}
Is this not possible at all? And if not, is there a good reason why not? Or am I just doing it wrong? Probably there is some solution using Lambda expressions?
You should give your static method its own generic type parameter :
public static <T> Vector<T> toVector1(Collection<T> coll) {
return new Vector<T>();
}
You were missing the generic type parameter declaration (<T>) before the return type of the method.
From the JDK documentation: "For static generic methods, the type parameter section must appear before the method's return type.". So it should look like
public static <E> Vector<E> toVector1(Collection<E> coll) {
return new Vector<E>();
}
// 1st try, same type E as class => Error: Cannot make a static reference to the non-static type E
public static Vector<E> toVector1(Collection<E> coll) {
return new Vector<E>();
}
This is because you've already defined a type-parameter, called E, on instance context and the compiler doesn't allow you to use it on static context.
// 2nd try, a new type X. => Error: X cannot be resolved to a type
public static Vector<X> toVector2(Collection<X> coll) {
return new Vector<X>();
}
Here, even though you don't use the instance type-parameter E, but another one, called X, the former is not correctly defined. When introducing method-scoped type-parameters, you have to do:
public static <X> Vector<X> toVector2(Collection<X> coll) {
return new Vector<X>();
}
// 3rd try, using wildcard. => Error: Cannot instantiate the type Vector<?>
public static Vector<?> toVector3(Collection<?> coll) {
return new Vector<?>();
}
The error is simply because the wildcard <?> can be only used in return-types and on initialization, but not on instantiation (like you've done).
// 4th try, using bounded wildcard. => Error: Cannot make a static reference to the non-static type E
public static Vector<? extends E> toVector4(Collection<? extends E> coll) {
return new Vector<E>();
}
The reason is the same as 1st try. You can fix this by having:
public static <X> Vector<? extends X> toVector4(Collection<? extends X> coll) {
return new Vector<X>();
}
However, note that when you use this method, you won't be able to add anything but null to the resulting list.

Java generics compile error involving Class<? extends T>

This program does not compile:
public class xx {
static class Class1<C> {
void method1(C p) {
}
}
static class Class2<T> extends Class1<Class<? extends T>> {
T object;
void method2() {
this.method1(this.object.getClass());
}
}
}
The error is:
xx.java:10: method1(java.lang.Class<? extends T>) in xx.Class1<java.lang.Class<? extends T>>
cannot be applied to (java.lang.Class<capture#215 of ? extends java.lang.Object>)
this.method1(this.object.getClass());
Why does this happen? Why does the compiler seemingly believe that object.getClass() returns Class<? extends Object> instead of Class<? extends T> ?
There is no upper bound set on T in your code, so ? extends T is really tantamount to ? extends Object. Just yesterday I played with a similar example and hit this barrier. I had
static <T> T newInstance(T o) throws Exception {
final Class<? extends T> c = o.getClass();
return c.newInstance();
}
and it complained with the same error. Consider this: the return type of Object.getClass() is Class<?> and the compiler will want to capture the ? into a concrete type. But instead, we would like not to capture the ?, but to "capture the upper bound" T -- and there is no such thing in Java's generics.
Object.getClass() is defined to return a Class<? extends |T|>, where T is the statically known type of the receiver (the object getClass() is called on). Take special note of the vertical bars, the erasure operator. The erasure of a type variable is the erasure of its leftmost bound. In your case that's the implicit bound Object. So you get back a Class<? extends Object>, not a Class<? extends T>.
Why is that?
Imagine T = List<Integer>, you could suddenly do the following without unchecked warning:
List<String> myStrings = new ArrayList<>();
List<Integer> myInts = new ArrayList<>();
List<Integer> myIntyStrings = myInts.getClass().cast(myStrings);
myIntyStrings.add(-1);
String myString = myStrings.get(0); // BANG!
But thankfully we do get a warning.. ;)
According to the documentation on getClass(), the returned object has type Class< ? extends |X| >, where |X| is the erasure of the type of the instance on which the method is called.
Therefore calling getClass() on an object of type T returns Class< ? extends Object >. We have no bound information about T in this API.
Usually APIs which use reflection on generic classes require that the client pass an additional argument of type Class< T > to the constructor or generic method in question.

Categories

Resources