Interface implementation propagation in Java generics - java

I have the following code (simplified, to show the problem core):
public interface IElement {}
public interface IDataSet<E extends IElement> {}
public interface IPropertyTranslator<D extends IDataSet<? super E>, E extends IElement> {}
public interface IElementTranslator<D extends IDataSet<?>> {}
public class AnimalElement implements IElement {}
public class AnimalDataSet implements IDataSet<AnimalElement> {}
public class AnimalPropertyTranslator implements IPropertyTranslator<AnimalDataSet, AnimalElement> {}
public class UniversalPropertyTranslator implements IPropertyTranslator<IDataSet<IElement>, IElement> {}
public class ElementTranslator<D extends IDataSet<? super E>, E extends IElement> implements IElementTranslator<D> {
public Collection<IPropertyTranslator<? super D, ? super E>> propertyTranslators = new HashSet<>();
}
public class Demo {
public static void demo() {
ElementTranslator<AnimalDataSet, AnimalElement> animalElementTranslator = new ElementTranslator<>();
animalElementTranslator.propertyTranslators.add(new AnimalPropertyTranslator());
animalElementTranslator.propertyTranslators.add(new UniversalPropertyTranslator());
}
}
Unfortunately, the last line of the demo method yields the following error: The method add(IPropertyTranslator<? super AnimalDataSet,? super AnimalElement>) in the type Collection<IPropertyTranslator<? super AnimalDataSet,? super AnimalElement>> is not applicable for the arguments (UniversalPropertyTranslator). Through random trials I discovered, that the problem is probably connected with the <D extends IDataSet<? super E>, E extends IElement> expression, although I still do not know how to fix it.
In the meantime the following variantion of the code works perfectly:
public interface IDataSet {}
public interface IPropertyTranslator<D extends IDataSet> {}
public interface IElementTranslator<D extends IDataSet> {}
public class AnimalDataSet implements IDataSet {}
public class AnimalPropertyTranslator implements IPropertyTranslator<AnimalDataSet> {}
public class UniversalPropertyTranslator implements IPropertyTranslator<IDataSet> {}
public class ElementTranslator<D extends IDataSet> implements IElementTranslator<D> {
public Collection<IPropertyTranslator<? super D>> propertyTranslators = new HashSet<>();
}
public class Demo {
public static void demo() {
ElementTranslator<AnimalDataSet> animalElementTranslator = new ElementTranslator<>();
animalElementTranslator.propertyTranslators.add(new AnimalPropertyTranslator());
animalElementTranslator.propertyTranslators.add(new UniversalPropertyTranslator());
}
}
I do not understand why the second generic part of the interface causes the code to behave differently.

The solution is slightly related to the "Producer Extends, Consumer Super" rule. In the problem code one need to change the following headers into those below:
public interface IPropertyTranslator<D extends IDataSet<? extends E>, E extends IElement> {}
public class UniversalPropertyTranslator implements IPropertyTranslator<IDataSet<? extends IElement>, IElement> {}
and everything would be working just fine.

Related

Java - Getting a more specific type than "Class<?>" for a parameterized generic class

I have a base interface that is parameterized using a type R extending the same base interface :
public interface IWidget<R extends IWidget<R>> {}
Then another interface is parameterized the same way :
public interface IWidgetManager<R extends IWidget<R>> {}
Finally, a class implements the second interface. This class will receive the IWidget implementation class when it will be declared :
public class MyWidgetManager<R extends IWidget<R>> implements IWidgetManager<R> {}
My question :
What is the more specific type that we can use to specify MyWidgetManager?
Class<?> works, of course :
public Class<?> ok() {
return MyWidgetManager.class;
}
But it is very generic and I'd like something more specific...
Those attempts don't compile:
public Class<? extends IWidgetManager<?>> fails() {
return MyWidgetManager.class;
}
==> Type mismatch: cannot convert from Class<MyWidgetManager> to Class<? extends IWidgetManager<?>>
public <R extends IWidget<?>> Class<? extends IWidgetManager<R>> fails() {
return MyWidgetManager.class;
}
==> Type mismatch: cannot convert from Class<MyWidgetManager> to Class<? extends IWidgetManager<R>>
public <R extends IWidget<R>> Class<? extends IWidgetManager<R>> fails() {
return MyWidgetManager.class;
}
==> Type mismatch: cannot convert from Class<MyWidgetManager> to Class<? extends IWidgetManager<R>>
public Class<? extends IWidgetManager<? extends IWidget<?>>> fails() {
return MyWidgetManager.class;
}
==> Type mismatch: cannot convert from Class<MyWidgetManager> to Class<? extends IWidgetManager<? extends IWidget<?>>>
Is there any way I can get a type more specific than Class<?> for MyWidgetManager.class?
UPDATE :
I changed the name of my interfaces. The final class is not a Widget itself, which wasn't clear in my original question... Sorry for the confusion.
UPDATE 2 :
Things are way easier when using concrete types, indeed.
This is really specific to my current situation, but I think I'll fix my "problem" by transforming MyWidgetManager<R extends IWidget<R>> implements IWidgetManager<R> into a WidgetManagerBase that doesn't implement anything. Then provide a default implementation with a concrete Widget class. Finally, the get method could be overriden easily (which is my main goal from the start, by the way!). So :
public interface IWidget<R extends IWidget<?>> {}
public interface IWidgetManager<R extends IWidget<R>> {}
public class WidgetManagerBase {}
// Default implementation
public class WidgetA implements IWidget<WidgetA> {}
public class AWidgetManager extends WidgetManagerBase implements IWidgetManager<WidgetA> {}
// default get method
public Class<? extends IWidgetManager<?>> getWidgetManagerClass() {
return AWidgetManager.class;
}
// The default get method then can be overriden with :
public class WidgetB implements IWidget<WidgetB> {}
public class BWidgetManager extends WidgetManagerBase implements IWidgetManager<WidgetB> {}
#Override
public Class<? extends IWidgetManager<?>> getWidgetManagerClass() {
return BWidgetManager.class;
}
Because of Java Type Erasure the most specific match for MyWidget.class is Class<? extends IWidget>:
public Class<? extends IWidget> test() {
return MyWidget.class;
}
If you want to be even more specific than you should extend MyWidget with concrete type parameters:
public class ConcreteBaseWidget implements IBaseWidget<ConcreteBaseWidget> {
}
public class ConcreteWidget extends MyWidget<ConcreteBaseWidget> {
}
And then all these methods will work:
public Class<? extends IWidget<? extends IBaseWidget<? extends IBaseWidget<?>>>> test1() {
return ConcreteWidget.class;
}
public Class<? extends IWidget<? extends IBaseWidget<ConcreteBaseWidget>>> test2() {
return ConcreteWidget.class;
}
public Class<? extends IWidget<? extends ConcreteBaseWidget>> test3() {
return ConcreteWidget.class;
}
I really think you're just looking for
public interface IBaseWidget<R extends IBaseWidget<R>> {}
and
public interface IWidget<R extends IWidget<R>> extends IBaseWidget<R> {} //not sure about this one.
and
public class MyWidget implements IWidget<MyWidget> {}
That way you can see MyWidget.class as Class<R>.
Is this what you are looking for, or do I misinterpret your intentions?
EDIT:
In that case,
public interface IWidgetManager<R extends IWidget<R>> {}
public class MyWidgetManager<R extends IWidget<R>> implements IWidgetManager<R> {}
could be replaced with
public interface IWidgetManager<R extends IWidget<R>, WM extends IWidgetManager<R, WM>> {}
public class MyWidgetManager<R extends IWidget<R>> implements IWidgetManager<R, MyWidgetManager> {}
because then you will be able to access MyWidgetManager.class as Class<WM>.

correct java syntax for generics with extends

this is a piece of code i'm struggling with.
public class Channel<T extends Something>{
public Channel(){}
public void method(T something){}
}
public class Manager{
private static ArrayList<Channel<? extends Something>> channels
= new ArrayList<Channel<? extends Something>>();
public static <T extends Something> void OtherMethod(T foo){
for(Channel<? extends Something> c : channels)
c.method(foo); // this does not work
}
}
The line that does not work gives me compiler error:
The method method(capture#1-of ? extends Something) in the type Channel<capture#1-of ? extends Something> is not applicable for the arguments (T)
I don't understand this error. If I remove all the generics in Manager class it is working but type unsafe.
How should I do this in correct Java?
You need a type parameter for your method public <T extends Something> void method(T foo)
public class Channel<T extends Something> {
public Channel() {
}
public <T extends Something> void method(T foo) {
}
}
public class Manager {
private static ArrayList<Channel<? extends Something>> channels = new ArrayList<Channel<? extends Something>>();
public static <T extends Something> void OtherMethod(T foo) {
for (Channel<? extends Something> c : channels)
c.method(foo); // this does not work
}
}
That's inherently unsafe.
What happens if you add a Channel<MyThing> to the list, then call OtherMethod() with a YourThing?
You should make the entire class generic (and make the members non-static), and use the same T for the channels and the parameter.

Java Bounded Generics: Type inference bug? (Method invocation, JLS 15.12.2.7)

For the following snippet of code:
import java.util.List;
public class Main {
interface Interface1<T> {}
interface Interface2<T> extends Interface1<T> {}
static class Bound {}
interface BoundedI1<T extends Bound> extends Interface1<T> {}
interface BoundedI2<T extends Bound> extends Interface2<T> {}
public static void main(String[] args) {
test((List<BoundedI2<?>>) null);
//test2((List<BoundedI2<?>>) null);
}
public static void test(List<? extends Interface2<? extends Bound>> list) { test2(list); }
public static void test2(List<? extends Interface1<? extends Bound>> list) {}
}
The compiler is OK with the first call, but complains if I uncomment the second. Is this a bug in the type inference system, or can someone explain why the inference rules in the JLS fail here?
Tested on both 6u43 and 7u45 Oracle JDKs.
UPDATE: Seems like eclipsec accepts it just fine. Unfortunately I can't really change up our toolchain :P, but it is interesting to find differences in the compilers.
The error message, as printed by ideone (cool tool btw):
Main.java:12: error: method test2 in class Main cannot be applied to given types;
test2((List<BoundedI2<?>>) null);
^
required: List<? extends Interface1<? extends Bound>>
found: List<BoundedI2<?>>
reason: actual argument List<BoundedI2<?>> cannot be converted to List<? extends Interface1<? extends Bound>> by method invocation conversion
UPDATE 2: This compiles fine, which indicates that the compiler does think BoundedI2<?> is assignable to Interface1<? extends Bound>, which seems to more directly contradict the JLS:
public class Main {
interface Interface1<T> {}
interface Interface2<T> extends Interface1<T> {}
static class Bound {}
interface BoundedI1<T extends Bound> extends Interface1<T> {}
interface BoundedI2<T extends Bound> extends Interface2<T> {}
public static void main(String[] args) {
test((List<BoundedI2<?>>) null);
//test2((List<BoundedI2<?>>) null);
test3((BoundedI2<?>) null);
}
public static void test(List<? extends Interface2<? extends Bound>> list) { test2(list); }
public static void test2(List<? extends Interface1<? extends Bound>> list) {}
public static void test3(Interface1<? extends Bound> instance) {}
}
It looks like the command line compiler have some difficulties handling at the same time
the fact that BoundedI2 is generic on T which must be a "Bound"
the fact that Interface2 is extending Interface1
At least without instanciating BoundedI2 correctly. What is strange indeed is that Eclipse configured on the same JDK compiles it just fine... Note that Eclipse uses it's internal compiler in order to handle incremental recompulation while you type, so it does not invoke the JDK's compiler at all (see org/eclipse/jdt/internal/compiler package).
This modification makes it compile ok both in Eclipse and in the command line, by forcing BoundedI2 to be on a concrete type rather than on a type inference:
import java.util.List;
public class PerfTest {
interface Interface1<T> {}
interface Interface2<T> extends Interface1<T> {}
static class Bound {}
interface BoundedI1<T extends Bound> extends Interface1<T> {}
interface BoundedI2<T extends Bound> extends Interface2<T> {}
static class Actual extends Bound {}
public static void main(String[] args) {
test((List<BoundedI2<Actual>>) null);
test2((List<BoundedI2<Actual>>) null);
}
public static void test(List<? extends Interface2<? extends Bound>> list) { test2(list); }
public static void test2(List<? extends Interface1<? extends Bound>> list) {}
}
Currently tracking bug report at OpenJDK, with some comments for insight: https://bugs.openjdk.java.net/browse/JDK-8051807

How to define bound type parameters in Java inner class

public class TowerOfHanoi<E> {
private class Disk<T extends Comparable<E>> {
}
private class Peg<S extends Disk<T extends Comparable<E>>> extends Stack<Disk<T extends Comparable<E>>> {
}
}
With the above code, I'm getting the following compilation error.
Syntax error on token "extends", , expected
However, if I change the definition of Peg as follows, it works:
private class Peg<T extends Disk<? extends Comparable<E>>> extends Stack<Disk<? extends Comparable<E>>> {
}
I don't want to use a wildcard. Is there a way to change that to a named parameter?
You can't use generics like that. Simply pass the type (not the bound) to the extended type.
This compiles:
public class TowerOfHanoi<E> {
private class Disk<T extends Comparable<E>> {
}
private class Peg<T extends Disk<Comparable<E>>> extends Stack<Disk<Comparable<E>>> {
}
}
Is there any reason why you can't make E comparable?
public class TowerOfHanoi<E extends Comparable<? super E>>
{
private class Disk implements Comparable<Disk> { }
private class Peg extends Stack<Disk> { }
}

Java - What is the correct signature for this generic class?

Here is a class I have :
public class ProxyDAO<T extends DAO<? extends Model>> implements DAO<? extends Model> {
...
}
The compiler complains that A supertype may not implement a wildcard.
I have tried this:
public class ProxyDAO<T extends DAO<? extends Model>> implements DAO<Model> {
...
}
But subclasses of Model are now not allowed.
How can I tell the compiler that ProxyDAO implements the DAO interface and that this interface would be accept a Model class or any subclasses or Model ?
DAO.java
public interface DAO<T extends Model> {
T findByPK(Object pk);
}
Use the following:
public class ProxyDAO<T extends Model> implements DAO<T>
And if you need to allow for supclasses of DAO:
public class ProxyDAO<T extends Model, V extends DAO<T>> implements DAO<T>
Do something as
class ProxyDao<M extends Model, T extends Dao<M>> implements Dao<M> {
}
I understood. In implements clause there is no possibility to set undefined type except as using two generic parameters. You can set there actual type like Model. So this code will work:
class Model{}
class SubModel extends Model{}
interface DAO<T extends Model> {
T findByPK(T pk);
}
class ProxyDAO<T extends DAO<? extends Model>> implements DAO<Model> {
public ProxyDAO() {
findByPK(new SubModel());
}
#Override
public Model findByPK(Model pk) {
throw new UnsupportedOperationException("Not supported yet.");
}
}

Categories