Как я могу создать список, используя дженерики в Java?

Пожалуйста, обратите внимание на следующий фрагмент:

public interface MyInterface {

    public int getId();
}

public class MyPojo implements MyInterface {

    private int id;

    public MyPojo(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

}

public ArrayList<MyInterface> getMyInterfaces() {

    ArrayList<MyPojo> myPojos = new ArrayList<MyPojo>(0);
    myPojos.add(new MyPojo(0));
    myPojos.add(new MyPojo(1));

    return (ArrayList<MyInterface>) myPojos;
}

Оператор return выполняет приведение, которое не компилируется. Как я могу преобразовать список myPojos в более общий список, без необходимости просматривать каждый элемент списка?

Спасибо


person Ry.    schedule 19.03.2009    source источник


Ответы (5)


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

public ArrayList<? extends MyInterface> getMyInterfaces() {    
    ArrayList<MyPojo> myPojos = new ArrayList<MyPojo>(0);
    myPojos.add(new MyPojo(0));
    myPojos.add(new MyPojo(1));

    return myPojos;
}

Это предотвратит попытку вызывающей стороны добавить в список другие реализации интерфейса. В качестве альтернативы вы можете просто написать:

public ArrayList<MyInterface> getMyInterfaces() {
    // Note the change here
    ArrayList<MyInterface> myPojos = new ArrayList<MyInterface>(0);
    myPojos.add(new MyPojo(0));
    myPojos.add(new MyPojo(1));

    return myPojos;
}

Как обсуждалось в комментариях:

  • Возврат коллекций с подстановочными знаками может быть неудобным для вызывающих абонентов
  • Обычно лучше использовать интерфейсы вместо конкретных типов для возвращаемых типов. Таким образом, предлагаемая подпись, вероятно, будет одной из:

    public List<MyInterface> getMyInterfaces()
    public Collection<MyInterface> getMyInterfaces()
    public Iterable<MyInterface> getMyInterfaces()
    
person Jon Skeet    schedule 19.03.2009
comment
Второе решение лучше ИМХО. Возврат подстановочных знаков обычно считается плохой практикой, т. к. он ограничивает клиентский код. В этом случае с ArrayList‹? extends MyInterface› можно было только читать из списка, и нельзя было ничего добавить к нему. - person Julien Chastang; 19.03.2009
comment
Конечно, это может быть то, что нужно - мы просто не знаем. (Почти наверняка следует также использовать List‹T› вместо ArrayList‹T› или, возможно, просто Iterable‹T› или Collection‹T›.) - person Jon Skeet; 19.03.2009
comment
+1 за использование более общих типов (например, List‹T›, Collection‹T›). Но возврат подстановочных знаков редко является тем, что вам нужно. Если вы склоняетесь к неизменности, есть лучшие способы сделать это. Нафталин и Вадлер рассказывают об этом в Java Generics and Collections. - person Julien Chastang; 19.03.2009
comment
Обработка дженериков в Java ужасна по сравнению с C#. Вполне допустимо добавить Собаку в список Животных. - person csmith; 02.10.2016
comment
@csmith: совершенно добавить Dog к List<Animal>. Я согласен с тем, что дженерики С# значительно лучше, но вторая часть вашего комментария не соответствует IMO. - person Jon Skeet; 02.10.2016

Лучше всего выбрать правильный тип с самого начала, однако, чтобы ответить на ваш вопрос, вы можете использовать стирание типа.

return (ArrayList<MyInterface>) (ArrayList) myPojos;

person Peter Lawrey    schedule 19.03.2009
comment
IMO, это должен быть ответ, так как в некоторых случаях вы просто не можете добавить элементы в коллекцию базового типа: подумайте о наборе результатов JPA из запросов, у вас есть список объектов JPA, и если вы хотите вернуться этот список вызывающему абоненту с использованием некоторого абстрагирующего интерфейса над сущностью (независимо от постоянства), совет Питера - самый способ - person Shmil The Cat; 25.06.2013

Вы должны делать:

public ArrayList<MyInterface> getMyInterfaces() {   
   ArrayList<MyInterface> myPojos = new ArrayList<MyInterface>(0);    
   myPojos.add(new MyPojo(0));    
   myPojos.add(new MyPojo(1));    
   return myPojos;
}
person Jacob Schoen    schedule 19.03.2009

В этом случае я бы сделал так:

public ArrayList<MyInterface> getMyInterfaces() {

    ArrayList<MyInterface> myPojos = new ArrayList<MyInterface>(0);
    myPojos.add(new MyPojo(0));
    myPojos.add(new MyPojo(1));

    return myPojos;
}

MyPojo имеет тип MyInterface (поскольку он реализует интерфейс). Это означает, что вы можете просто создать список с нужным вам интерфейсом.

person Tobias Langner    schedule 19.03.2009

Попробуйте использовать интерфейсы везде, кроме создания экземпляров, и проблемы исчезнут:

public List<MyInterface> getMyInterfaces()
{
    List<MyInterface> myInterfaces = new ArrayList<MyInterface>(0);
    myInterfaces.add(new MyPojo(0));
    myInterfaces.add(new MyPojo(1));

    return myInterfaces;
}

Как уже говорили другие, использование MyInterface решает вашу проблему. Также лучше использовать интерфейс List вместо ArrayList для возвращаемых типов и переменных.

person starblue    schedule 19.03.2009
comment
Однако исправляет это не использование List, а использование MyInterface вместо MyPojo в качестве аргумента типа. - person Jon Skeet; 19.03.2009
comment
Да, это вводило в заблуждение, сейчас исправил. - person starblue; 19.03.2009