Как исправить эту проблему с подстановочными знаками / абстрактной неоднозначностью в Java Generics? - PullRequest
1 голос
/ 06 октября 2010

Код ниже является упрощенной версией шаблона, который использует мой проект.Стандартный шаблон, который мы используем, - иметь Writer для каждого типа объекта.Для подтипов одного абстрактного типа (в данном примере Animal) я бы хотел, чтобы перечисление служило поиском правильного автора.

abstract class Writer<T> {
    abstract void write(T value);
}

abstract class Animal {
    abstract AnimalType getType();
}

class Cat extends Animal {
    AnimalType getType() { return AnimalType.CAT; }
}

class CatWriter extends Writer<Cat> {
    void write(Cat value) { }
}

// The AnimalType stores a reference to the correct writer for the Animal subclass
enum AnimalType {
    CAT(new CatWriter());

    Writer<? extends Animal> writer;
    Writer writerThatWorksWithWarning;
    Writer<Animal> writerThatWorksButCantBeAssigned;

    AnimalType(Writer<? extends Animal> writer) {
        this.writerThatWorksWithWarning = writer;
        this.writer = writer;

        // ERROR: Incompatible Types
        this.writerThatWorksButCantBeAssigned = writer;

    }
}

Пример использования:

class Test {
    public static void main(String... args) {
        Animal value = new Cat();

// ERROR: write (capture<? extends Animal) in Writer cannot be applied to (Animal)
        value.getType().writer.write(value);

// WARNING: Unchecked call
        value.getType().writerThatWorksWithWarning.write(value);

// This line works fine here - but can't be assigned above
        value.getType().writerThatWorksButCantBeAssigned.write(value);
    }
}

Я думаю, что моя проблема похожа на проблему в этом вопросе: Generics Java с подстановочным знаком , однако я не могу сказать, как ее решить.ошибки и предупреждения я получаю в комментариях.

Есть идеи?

Ответы [ 3 ]

2 голосов
/ 06 октября 2010

Я думаю, что проблема в том, что вы не можете представить иерархию типов с помощью перечисления, поэтому нет способа сказать системе типов, что для enum { CAT, DOG; } значение CAT должно быть равно CAT extends Animal и * 1004.* печатает до DOG extends Animal.Итак, поскольку у вас уже есть иерархия классов, почему бы не использовать это?то есть что-то вроде:

public interface Writer<T> {
    public void write(T t);
}


public abstract class Animal<T extends Animal<T>> {
    public abstract Writer<T> getWriter()...
}

public class Cat extends Animal<Cat> {
    @Override
    public Writer<Cat> getWriter()...
}

Мне кажется, что вы действительно используете enum для чего-то вроде хеш-карты <Class, Writer<Class>>, своего рода встроенного синглтона.Вы можете сделать это, но только скрывая типы.

1 голос
/ 06 октября 2010

Я бы хотел, чтобы животные не знали авторов.в конце концов, это животные.

Вы можете иметь Map<Class,Writer>, и для каждой записи в нем вы утверждаете, что ключ Class<X> и значение Writer<X> относятся к одному и тому же типу X.Мы не можем выразить это отношение в типах, поэтому приведение должно выполняться в некоторых местах.Если поиск типа не удастся (скажем, Cat), попробуйте поискать снова с его супертипами (Animal)

Безопасный публичный API типа может быть разработан как

static public <T> void registerWriter(Class<T> type, Writer<T> writer)

static public <T> Writer<? super T> getWriter(Class<T> type)

Предположим, мы неу нас нет Writer, напрямую сопоставленного с Cat, но у нас есть Writer<Animal> для Animal, тогда этот писатель будет возвращен для Cat.class.Это нормально, потому что этот писатель принимает всех животных.

Этот удобный метод может быть предоставлен:

static public static void write(Object obj)

от типа объекта, можно найти подходящего писателя иписатель примет объект.

0 голосов
/ 06 октября 2010

Попробуйте вместо этого,

enum AnimalType {
    CAT(new CatWriter());

    private Writer<? extends Animal> writer;

    AnimalType(Writer<? extends Animal> writer) {
        this.writer = writer;
    }

    public Writer<Animal> getWriter() {
        return (Writer<Animal>)writer;
    }
}

Более того, я не уверен, что вы делаете.Но я верю, что Шаблон посетителя пригодится в этом случае.

Проблема с приведенным выше решением, код ниже сломает вещь.

    Animal cat = new Cat();
    Animal dog = new Dog();

    cat.getType().getWriter().write(cat);

    // java.lang.ClassCastException in the write() method's argument
    cat.getType().getWriter().write(dog);
...