I'm struggling to capture a wildcard when it is "nested in another wildcard".
Is it possible?
The code:
public class ConvolutedGenerics {
// listClass is a class implementing a List of some Serializable type
public void doSomethingWithListOfSerializables(
Class<? extends List<? extends Serializable>> listClass) {
// Capture '? extends Serializable' as 'T extends Serializable'
// The line does not compile with javac 7
captureTheWildcard(listClass); // <------ zonk here
}
// listClass is a class implementing a List of some Serializable type
private <T extends Serializable>
void captureTheWildcard(
Class<? extends List</* ? extends */T>> listClass) {
// Do something
}
}
compiled with javac 7 produces:
ConvolutedGenerics.java:18: error: method captureTheWildcard in class ConvolutedGenerics cannot be applied to given types;
captureTheWildcard(listClass);
^
required: Class<? extends List<T>>
found: Class<CAP#1>
reason: no instance(s) of type variable(s) T exist so that argument type Class<CAP#1> conforms to formal parameter type Class<? extends List<T>>
where T is a type-variable:
T extends Serializable declared in method <T>captureTheWildcard(Class<? extends List<T>>)
where CAP#1 is a fresh type-variable:
CAP#1 extends List<? extends Serializable> from capture of ? extends List<? extends Serializable>
1 error
Besides many more simpler cases I've found
Incompatible generic wildcard captures
Using Java wildcards
but I could not infer an answer for my problem from those.
It is not possible, as you probably already know.
Let me illustrate with a counter-example:
List<Integer> foo = Arrays.asList(1,2,3);
List<String> bar = Arrays.asList("hi","mom");
List<List<? extends Serializable>> baz = Arrays.asList(foo, bar);
doSomethingWithListOfSerializables(baz);
public void doSomethingWithListOfSerializables(
List<? extends List<? extends Serializable>> listList) {
captureTheWildcard(listList);
}
private <T extends Serializable>
void captureTheWildcard(
List<? extends List<T>> listList) {
// Do something
}
What should T be?
The problem with your code is that you're trying to call captureTheWildcard passing different typed parameter then defined here:
private <T extends Serializable> void captureTheWildcard(Class<? extends List<T>> listClass)
You should explicitly say in your method definition that parameter passed is actually of type of Class<? extends List<? extends Serializable>>or modify the type of listClass like this:
import java.util.List;
import java.io.Serializable;
public class ConvolutedGenerics {
// listClass is a class implementing a List of some Serializable type
public <T extends Serializable> void doSomethingWithListOfSerializables(
Class<? extends List<T>> listClass) {
// Capture '? extends Serializable' as 'T extends Serializable'
// The line does not compile with javac 7
captureTheWildcard(listClass); // <------ zonk here
}
// listClass is a class implementing a List of some Serializable type
private <T extends Serializable> void captureTheWildcard(Class<? extends List<T>> listClass) {
// Do something
}
}
Compiles well with javac 1.7.0_25
Related
I have a Storage class:
class Storage<E> {
void add(E e) {
// add element
};
void addAll(Iterable<? extends E> src) {
for (E e : src)
add(e);
}
}
There are two classes where class Child extends Parent:
Parent
class Parent implements Comparable<Parent> {
#Override
public int compareTo(Parent o) {
return 0; // some comparison logic
}
}
Child
class Child extends Parent {
}
Driver class:
import java.util.Arrays;
import java.util.List;
public class GenericTest {
public static void main(String[] args) {
/******** CASE 1 *********/
Storage<Parent> ds = new Storage<Parent>();
ds.add(new Parent());
ds.addAll(Arrays.asList(new Parent()));
// Type params are invariant.
// But List<Child> is possible due to bounded wildcard type on addAll
ds.addAll(Arrays.asList(new Child())); // Makes sense
/******** CASE 2 *********/
List<Child> t = Arrays.asList();
max(t);
}
static <T extends Comparable<T>> T max(List<T> list) {
return null; // Return null so code can compile
}
}
Because Storage is a generic class, the operations on its method makes sense; I get how case 1 is working.
In case 2, with above signature of max in GenericTest, I get the compilation error:
The method max(List<T>) in the type GenericTest is not applicable for the arguments (List<Child>)
I understand that Comparable<Child> is not a sub-type of Comparable<Parent> (Typed params are invariant).
So, I updated signature to
// update Comparable<T> to Comparable<? super T>
static <T extends Comparable<? super T>> T max(List<T> list)
Now, the code compiles and inferred signature is
<Child> Child GenericTest.max(List<Child> list)
This makes sense.
When I update the signature to
// update List<T> to List<? extends T>
static <T extends Comparable<T>> T max(List<? extends T> list)
the code compiles and inferred signature is
<Parent> Parent GenericTest.max(List<? extends Parent> list)
I couldn't understand how updating List<T> to List<? extends T> made the compiler infer the type Parent.
I mean, for a generic method (on which there is direct invocation using type Child), how did ? extends T helped compiler refer the parent class of Child? Note that this signature has Comparable<T> (and not Comparable<? super T).
Isn't extends about a type which is T itself and its sub-classes?
Edit:
Java 8 comes with updated type-inference rules.
The below is sufficient for type-inference in Java 8 and later, but not for Java 7 and below:
static <T extends Comparable<T>> T max(List<? extends T> list)
It gives compilation error:
Bound mismatch: The generic method max(List<? extends T>) of type GenericTest is not applicable for the arguments (List<Child>). The inferred type Child is not a valid substitute for the bounded parameter <T extends Comparable<T>>
The signaure
static <T extends Comparable<? super T>> void max(List<T> list)
is sufficient for Java 7, though.
To make things clearer, the class Child:
extends Parent
implements Comparable<Parent>
1) static <T extends Comparable<T>> T max(List<T> list)
T is Child
fails because Child is not a Comparable<Child>
2) static <T extends Comparable<? super T>> T max(List<T> list)
T is Child
? is Parent
it works because Child implements Comparable<Parent super Child>
3) static <T extends Comparable<T>> T max(List<? extends T> list)
T is Parent
? is Child
it works because Child extends Parent implements Comparable<Parent>
Here in case 3), finding a valid T class can be seen as: "find the first super class of Child that implements a Comparable of itself".
As in case 1), it cannot be Child because it is not a Comparable<Child>.
The first (and only) super class of Child that implements Comparable of itself is Parent.
I couldn't understand how updating List<T> to List<? extends T> made the compiler infer the type Parent.
List<T> forces T to be Child
List<? extends T> forces ? to be Child, not T
To me it fails with both JDK 8 and JDK 7, so I don't see the inconsistency which you describe between those JDks
JDK 8 compilation
JDK 7 compilation
As to why it fails it is called PECS (Producer Extends Consumer Super)
The following answer has helped me a lot to understand it.
If you say <? extends SomeType>, then you wanna describe a ‘box’ that
is the same size or smaller than the ‘SomeType’ box.
Taking in consideration the aforementioned, when you described max method as
static <T extends Comparable<T>> T max(List<? extends T> list)
With your declaration List<? extends T> list you need a container of the same or smaller than T. Which could be Child as you use it.
List<Child> t = Arrays.asList();
so this should have compiled.
However the return type is of <T extends Comparable<T>> T which then is translated into Child extends Comparable<Child>.
Java however has a limitation that prevents you from implementing the same generic interface (in your case Comparable) with different type parameters.
That is why the error says
required: java.util.List<? extends T>
found: java.util.List<Child> reason: inferred type does not
conform to declared bound(s)
inferred: Child
bound(s): java.lang.Comparable<Child>
Solution
You should have declared the return type with <T extends Comparable<? super T>>
Contra-variance: ? super T ( the family of all types that are
supertypes of T) - a wildcard with a lower bound. T is the lower-most
class in the inheritance hierarchy.
from a previous answer on PECS
In that case it would have compiled as it would have set the bound to Comparable<Parent>.
I found some weird behavior for java generics. I can't use parametrized class, which implements another parametrized class, in parametrized method.
It sounds messy, so let's look at the code:
class A {
public<RESP extends Serializable, REQ extends Serializable> RESP sendData(REQ req, Class<? extends I<RESP, REQ>> c) {
return null;
}
}
interface I<RESP extends Serializable, REQ extends Serializable> {
}
class B implements I<String, Integer> {
A a;
public void test() {
a.sendData(1, getClass());
}
}
It compiles fine. But we can break it easily. Just add a parameter variable to class B:
class B<T> implements I<String, Integer>
Now a.sendData(1, B.class); has the compiler error:
method sendData in class A cannot be applied to given types;
required: REQ,Class<? extends I<RESP,REQ>>
found: int,Class<B>
reason: cannot infer type-variable(s) RESP,REQ
(argument mismatch; Class<B> cannot be converted to Class<? extends I<RESP,REQ>>)
where REQ,RESP are type-variables:
REQ extends Serializable declared in method <RESP,REQ>sendData(REQ,Class<? extends I<RESP,REQ>>)
RESP extends Serializable declared in method <RESP,REQ>sendData(REQ,Class<? extends I<RESP,REQ>>)
I just added unused parameter to class signature!
Can anyone explain why this happens and is there way to use type variable in class B?
Actualy my goal is signature for class B like this:
class B<Data> implements I<String, ? extends List<Data>>
By the way, I try this code in Java 8.
I use two Interfaces:
public interface Receiver<T> {
public void receive(T obj);
public Set<Class<? extends T>> getInterests();
}
public interface Distributor<T> extends Receiver<T> {
public void register(Receiver<T> receiver);
}
My problem that I want to register a Distributor in a Distributor e.g.
Distributor<Object> ==register==> Distributor<String>
My first thought was to change the register method to register(Receiver<? extends T> receiver). But if I want to get the Classes the receiver is intrested in the Method getInterests would return something like
Set<Class<? extends ? extends T>>.
Indirect I get something like Set<Class<? extends T>> but I experienced that transitiv wildcards are not possible in Java.
Got anyone an idea?
EDIT: As an example:
public void register(Receiver<? extends T> receiver){
Set<Class<? extends T>> interests = receiver.getInterests();
//Problem because receiver.getInterests is
//Set<Class<? extends ? extends T>>
...
}
Your problem is that Java generics are completely invariant, unless you make them variant using ? extends or ? super wildcards.
A Set<Class<? extends T>> can only hold expressions of exactly the compile-time type Class<? extends T>. Class<String> is not the same type as Class<? extends T> (even though it is convertible to that type).
You want a set that can hold any type that is convertible to Class<? extends T>.
That would be a Set<? extends Class<? extends T>>
You could add a helper method that uses a type variable instead of wildcard
public void register(Receiver<? extends T> receiver)
{
register2(receiver);
}
private <S extends T> void register2(Receiver<S> receiver)
{
Set<Class<? extends S>> interests = receiver.getInterests();
...
}
On the other hand, method
public Set<Class<? extends T>> getInterests();
is probably intended to return a covariant Set, or, read-only set. Ideally wildcard should be used
public Set<? extends Class<? extends T>> getInterests();
but I know, there are too many damn wildcards...
Consider the following code:
public interface A {};
public class AImpl implements A {};
public interface B {};
public class BImpl implements B {};
public interface Service{
Map<? extends A, List<? extends B>> get();
}
Why does the following implementation of Service not compile?
public class ServiceImpl implements Service {
public Map<AImpl, List<BImpl>> get() {
return null;
}
}
Compiler error:
The return type is incompatible with Service.get()
But the following code compile:
public interface Service{
List<? extents B> get();
}
public class ServiceImpl implements Service{
public List<BImpl> get(){
return null;
}
}
Because <? extends BaseType> means "some unspecified sub-type BaseType", and class Sub extends BaseType, while sub-type of BaseType, is not it. Read Java Generics FAQ, in particular starting from Wildcard Capture section, for more details.
You should generify your code properly:
public interface Service<K extends A, V extends B> {
Map<K, List<V>> get();
}
public class ServiceImpl implements Service<AImpl, BImpl> {
#Override
public Map<AImpl, List<BImpl>> get() {
return null;
}
}
The return type of the Service#get() method is specified as
Map<? extends A, List<? extends B>> get();
And you are trying to return a
Map<AImpl, List<BImpl>>
You can use covariant return types. And it seems like you thought this would be the case here. But the problem is that this covariance does not apply to the generic type parameters. Although List<BImpl> is a subtype of List<? extends B>, this does not mean that Map<AImpl, List<BImpl>> is a subtype of Map<? extends A, List<? extends B>>.
A structurally similar but simpler case is that List<Integer> is not a subtype of List<Number>.
You could change the return type in the Service interface to
Map<? extends A, ? extends List<? extends B>> get();
to make it work.
ServiceImpl cannot be an interface because your Implementation cannot be in an interface; Change it to a base base class and try it.
What do you write?
Map<? extents A, List<? extents B> get();
java doesn't know anything about extents
At least use extends
Having these generic interface and class:
interface TestIntf<T extends TestIntf<T>>
{
void test(T param);
}
class TestImpl<T extends TestIntf<T>> implements TestIntf<T>
{
#Override
public void test(T param) { System.out.println(param); }
}
This fails:
Class<? extends TestIntf<?>> clz = TestImpl.class;
(Type mismatch: cannot convert from Class<TestImpl> to Class<? extends TestIntf<?>>)
Why? How to properly provide a reference to TestImpl class to match Class<? extends TestIntf<?>>?
You can't. Use an unsafe cast.
Class<? extends TestIntf<?>> clz = (Class<? extends TestIntf<?>>) TestImpl.class;
or don't use the inner generics:
Class<? extends TestIntf> clz = TestImpl.class;
Update: When it regards annotations, there is nothing you can do - the annotation has to be changed. You cannot have a class literal represent a generic type.