Skip to content

Instantly share code, notes, and snippets.

@odrotbohm
Created March 29, 2018 07:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save odrotbohm/a52c430f84d4f70fcda6cb494dc15981 to your computer and use it in GitHub Desktop.
Save odrotbohm/a52c430f84d4f70fcda6cb494dc15981 to your computer and use it in GitHub Desktop.
public class Sample {
public static void main(String[] args) {
Foo<? extends Bar<?>> foo = new Foo<>();
// Compiles
Iterable<?> first = foo.someMethod();
DedicatedWrapper<? extends Bar<?>> dedicatedWrapper = foo.someOtherMethod();
// Does not compile:
// Type mismatch: cannot convert from Iterable<Sample.SomeWrapper<capture#2-of ? extends Sample.Bar<?>>>
// to Iterable<Sample.SomeWrapper<? extends Sample.Bar<?>>>
Iterable<SomeWrapper<? extends Bar<?>>> second = foo.someMethod();
}
static class Foo<B extends Bar<B>> {
Iterable<SomeWrapper<B>> someMethod() {
return Collections.emptySet();
}
DedicatedWrapper<B> someOtherMethod() {
return null;
}
}
interface Bar<B extends Bar<B>> {}
interface SomeWrapper<B extends Bar<B>> {}
interface DedicatedWrapper<B extends Bar<B>> extends Iterable<SomeWrapper<B>> {}
}
@schauder
Copy link

schauder commented Mar 29, 2018

Can't claim I understand what's going on and don't know if it helps, but my IDE suggests:

	Iterable<? extends SomeWrapper<? extends Bar<?>>> second = foo.someMethod();

I also have a hunch that it might be related to https://stackoverflow.com/q/46050311/66686

@winterbe
Copy link

Foo<B extends Bar<B>> is kinda weird imo. What would even be a valid type for B besides ??

If you could change declaration of Foo to Foo<B extends Bar<?>> then it compiles as expected.

@odrotbohm
Copy link
Author

odrotbohm commented Mar 29, 2018

It's needed to allow the following:

interface Foo<B extends Bar<B>> {
  B someMethod();
}

class ConcreteB implements Bar<ConcreteB> { … }

Foo<ConcreteB> foo = …
ConcreteB b = foo.someMethod();

I.e. it allows defining methods that will return a dedicated suptype of the generic type declared. Intensively used in Spring Data, just simplified here.

@kalgon
Copy link

kalgon commented Mar 29, 2018

Can't you capture the parameter like this?

    public static void main(String[] args) {
        Foo<? extends Bar<?>> foo = new Foo<>();
        capture(foo);
    }

    public static <B extends Bar<B>> void capture(Foo<B> foo) {
        Iterable<SomeWrapper<B>> second = foo.someMethod();
        // do something with second
    }

@philwebb
Copy link

philwebb commented Mar 29, 2018

I think it boils down to the fact that the compiler doesn't know that the first and second ? in this line refer to the same thing Foo<? extends Bar<?>> foo = new Foo<>();

In the past when I've had this kind of problem I've extracted the method and added a real generic:

public static void main(String[] args) {
	typed();
}

private static <T extends Bar<T>> void typed() {
	Foo<T> foo = new Foo<>();

	// Compiles
	Iterable<?> first = foo.someMethod();
	DedicatedWrapper<T> dedicatedWrapper = foo.someOtherMethod();

	// Compiles
	Iterable<SomeWrapper<T>> second = foo.someMethod();
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment