2

I've got an issue with a generic framework I'm writing. Can someone explain to me, why my code does not compile? I've tried to show it with this simple example. (UPDATED EXAMPLE)

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;

public class TestGeneric {

    public static void main(String... sss) throws Exception {

        Dao dao = new Dao("Hello");
        dao.extend();

        System.out.println(dao.getHelloWorld());
    }
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface TestAnnotation {

    public Class<? extends AbstractCommand<? extends AbstractDao>>[] commands() default {};
}

abstract class AbstractDao {

    public void extend() throws Exception {

        for (Field field : this.getClass().getDeclaredFields()) {

            if (field.isAnnotationPresent(TestAnnotation.class)) {

                TestAnnotation annotation = field.getAnnotation(TestAnnotation.class);

                for (Class<? extends AbstractCommand<? extends AbstractDao>> commandClass : annotation.commands()) {

                    AbstractCommand<? extends AbstractDao> command = commandClass.newInstance();
                    command.doSomething(this);
                }
            }
        }
    }
}

class Dao extends AbstractDao {

    @TestAnnotation(commands = { Command.class })
    private String hello;

    private String world;

    public Dao(String hello) {
        this.hello = hello;
    }

    public String getHello() {
        return this.hello;
    }

    public void setWorld(String world) {
        this.world = world;
    }

    public String getHelloWorld() {
        return this.hello + " " + this.world;
    }
}

abstract class AbstractCommand<T extends AbstractDao> {
    public abstract void doSomething(T t);
}

class Command extends AbstractCommand<Dao> {

    @Override
    public void doSomething(Dao t) {

        if (t.getHello().equals("Hello")) {
            t.setWorld("World");
        }
    }
}

As soon as I make following changes ...

abstract class AbstractCommand<T extends AbstractDao> {
    public abstract void print(AbstractDao t);
}

class Command extends AbstractCommand<Dao> {

    @Override
    public void doSomething(AbstractDao t) {

        Dao dao = (Dao) t;

        if (dao.getHello().equals("Hello")) {
            dao.setWorld("World");
        }
    }
}

... everything works fine, but then I have to cast AbstractDao all the time.

As far as I can say, everything should be save, but I keep getting this error.

The method print(capture#3-of ? extends AbstractDao) in the type AbstractCommand is not applicable for the arguments (Dao)

But Dao extends AbstractDao, so where exactly is the problem?

I already found this question generics error: not applicable for the arguments but I'm not sure whether or not it is the same problem I have.

My guess is that it has something to do with 'Because the Java compiler erases all type parameters in generic code, you cannot verify which parameterized type for a generic type is being used at runtime'

Does anyone have a solution for this problem?

Thanks!

Community
  • 1
  • 1
BenGe89
  • 141
  • 1
  • 7
  • 5
    What do you want to do? `AbstractCommand extends AbstractDao>` means it is an `AbstractCommand` of **some** unknown type that extends `AbstractDao`. This could or could not be `Dao`, the compiler can't know. – Tunaki Mar 19 '16 at 17:50
  • Possible duplicate http://stackoverflow.com/q/2723397/2891664. `AbstractCommand` is a consumer, not a producer. – Radiodef Mar 19 '16 at 18:13
  • I've updated my example to make it more clear, what I try to achive. Is there any way Dao cannot extend AbstractDao? – BenGe89 Mar 19 '16 at 18:26

1 Answers1

3

You're using an AbstractCommand reference.

Try this

((Command)command).print(new Dao("Hello World"));
Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89
  • I don't know, whether its an instance of Command or of something else extending AbstractCommand the time I'm calling the method. – BenGe89 Mar 19 '16 at 17:53
  • @BenGe89 In this case you know its command since you're using `Command.class` – Yassin Hajaj Mar 19 '16 at 17:53
  • @BenGe89 Try writing `Command command = clazz.newInstance();` instead. Then the type mismatch error occurs at that point and may be more enlightening. _You_ know it's a `Command` but the static type of `clazz` is `AbstractCommand`, so an instantiation would require a downcast. – Jim Garrison Mar 19 '16 at 17:59
  • *"I don't know, whether its an instance of Command or of something else extending AbstractCommand the time I'm calling the method."* Then how do you know you can call `print` with a `Dao`? What if it's an `AbstractCommand`? – Radiodef Mar 19 '16 at 18:11
  • @Radiodef I've updated the example. Hopefully my problem is more clear now. I don't know the class extending AbstractDao as well as I don't know the class extending AbstractCommand. – BenGe89 Mar 19 '16 at 19:04