Универсальное программирование на Java с неизвестным универсальным интерфейсом - PullRequest
2 голосов
/ 04 сентября 2011

Я использую несколько интерфейсов с универсальными типами.При его объединении у меня возникают некоторые проблемы, когда мне приходится использовать их из части кода, которая не знает о конкретном типе универсального параметра.

Предположим, у меня есть следующий интерфейс:

public interface MyObjectInterface<T extends Number> {}

Объект, реализующий этот интерфейс, хранится в универсальной коллекции с тем же универсальным типом:

public interface MyCollectioninterface<T extends Number> {
    public void updateObject(MyObjectInterface<T> o);
}

Конкретные экземпляры MyCollectionInterface содержат несколько MyObjectInterface одного и того же универсального параметра:

public class ConcreteCollection<T extends Number> implements
 MyCollectionInterface<T> {
    List<MyObjectInterface<T>> list;
    public void updateObject(MyObjectInterface<T> o){}

}

Теперь у меня есть несколько вопросов о том, как использовать эти универсальные интерфейсы из клиентского класса, который (и не должен) не знать о конкретном типе универсальных шаблонов.

Предположим, у меня есть следующий класс:

public class ClientClass{

    private MyCollectionInterface<?> collection;  //1st possibility
    private MyCollectionInterface collection;  //2nd possibility

    public ClientClass(MyCollectionInterface<?> collection){
        this.collection = collection;
    }

    public void foo(MyObjectInterface<?> o){
         this.collection.updateObject(o);  //this doesn't compile
    }
    public void foo(MyObjectInterface<? extends Number> o){
         this.collection.updateObject(o);  //this doesn't compile either
    }
    public void bar(MyObjectInterface o){
         MyObject b = o; //warning
         this.collection.updateObject(o);  //this compile but with warnings
    }
}

Первый вопрос :

  1. Учитывая тот факт, что ClientClass не заботится оКакой конкретный тип, расширяющий Number, является коллекцией, я должен объявить коллекцию с или без "?"?Если я использую вторую версию, я получаю следующее предупреждение:

MyCollectionInterface является необработанным типом.Ссылки на универсальный тип LatticeInterface должны быть параметризованы

Второй вопрос :

  1. Почему метод foo не компилируется?

Третий вопрос :

  1. Похоже, мне нужно использовать подпись бара для вызова метода updateObject.В любом случае, это решение выдает предупреждение при попытке назначить параметр MyObjectInterface, как в первом вопросе.Могу ли я удалить это предупреждение?

Последние вопросы :

  1. Я делаю что-то странное с этими универсальными интерфейсами, и я должен реорганизовать свой код?
  2. Должен ли я действительно заботиться обо всех этих предупреждениях?
  3. Как я могу использовать безопасный универсальный интерфейс из класса, где я не знаю его конкретного типа?

1 Ответ

1 голос
/ 04 сентября 2011

Хорошо, я немного поиграл с вашим кодом и пришел к выводу.

Проблема в том, что ваш ConcreteCollection (и его интерфейс MyCollectionInterface) объявляют метод updateObject как получающий аргумент типа MyObjectInterface<T> (где T extends Number) - обратите внимание, что тип является конкретным ( не подстановочный знак).

Теперь в вашем клиентском классе вы получаете коллекцию и сохраняете ее как MyCollectionInterface<?> , но экземпляр, который передается конструктору ClientClass, будет иметь конкретный тип, например:

new ClientClass(new ConcreteCollection<Integer>());

Это означает, что метод updateObject этого экземпляра будет принимать только аргумент типа MyCollectionInterface<Integer>.

Затем в методе foo вы пытаетесь передать MyObjectInterface<?> в updateObject, но поскольку компилятор не знает, какой универсальный тип принимает ваша коллекция (это может быть Integer, как в моем примере, но это также может быть Double или любой другой тип, который расширяет Number), он не допустит прохождения любого объекта .

Короче говоря, если вы объявите свою ссылку как MyCollectionInterface<?>, вы не сможете набрать updateObject. Итак, у вас есть два варианта:

1) Выберите конкретный тип и придерживайтесь его:

private MyCollectionInterface<Number> collection;

public ClientClass(MyCollectionInterface<Number> collection){
    this.collection = collection;
}

public void foo(MyObjectInterface<Number> o){
     this.collection.updateObject(o);  //compiles
}

Но тогда вы ограничиваете коллекции, которые вы можете получить в конструкторе (что может быть неплохой идеей), или:

2) Измените свой интерфейс так, чтобы он принимал подстановочный тип:

public interface MyCollectionInterface<T extends Number> {
    public void updateObject(MyObjectInterface<? extends Number> o);
}

public class ConcreteCollection<T extends Number> implements MyCollectionInterface<T> {
    List<MyObjectInterface<T>> list;
    public void updateObject(MyObjectInterface<? extends Number> o) {}
}

private MyCollectionInterface<?> collection;

public ClientClass(MyCollectionInterface<?> collection){
    this.collection = collection;
}

public void foo(MyObjectInterface<?> o){
     this.collection.updateObject(o);  //compiles
}

Также обратите внимание, что даже в 2) вы все равно можете столкнуться с той же проблемой при реализации метода updateObject, если вы не объявите свой list как-то так (с ArrayList для пример):

List<MyObjectInterface<? extends Number>> list = new ArrayList<MyObjectInterface<? extends Number>>();

В этом случае вы также можете удалить <T extends Number> из MyCollectionInterface и ConcreteCollection, поскольку T больше не используется.

@ Ваши последние вопросы:

1) Вероятно, да
2) Вы должны
3) Вы не можете, если вам действительно все равно, какие объекты вы храните в коллекции, вам следует вообще отказаться от генериков.

Извините за длинный ответ, надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...