Java с использованием обобщений со списками и интерфейсами - PullRequest
5 голосов
/ 25 июня 2011

Хорошо, вот моя проблема:

У меня есть список, содержащий интерфейсы - List<Interface> a - и список интерфейсов, расширяющих этот интерфейс: List<SubInterface> b. Я хочу установить a = b. Я не хочу использовать addAll() или что-либо, что будет стоить больше памяти, поскольку то, что я делаю, уже очень затратно. Мне буквально нужно уметь сказать a = b. Я пробовал List<? extends Interface> a, но тогда я не могу добавить интерфейсы в список a, только субинтерфейсы. Есть предложения?

Я хочу иметь возможность сделать что-то вроде этого:

List<SubRecord> records = new ArrayList<SubRecord>();
//add things to records
recordKeeper.myList = records;

Класс RecordKeeper - это тот, который содержит список интерфейсов (НЕ подинтерфейсов)

public class RecordKeeper{
    public List<Record> myList;
}

Ответы [ 6 ]

6 голосов
/ 25 июня 2011

Это работает:

public class TestList {

    interface Record {}
    interface SubRecord extends Record {}

    public static void main(String[] args) {
        List<? extends Record> l = new ArrayList<Record>();
        List<SubRecord> l2 = new ArrayList<SubRecord>();
        Record i = new Record(){};
        SubRecord j = new SubRecord(){};

        l = l2;
        Record a = l.get( 0 );
        ((List<Record>)l).add( i );       //<--will fail at run time,see below
        ((List<SubRecord>)l).add( j );    //<--will be ok at run time

    }

}

Я имею в виду, что оно компилируется, но вам придется разыграть свой List<? extends Record>, прежде чем добавлять что-либо внутрь.Java разрешит приведение, если тип, который вы хотите привести, является подклассом Record, но он не может угадать, какой это будет тип, вы должны указать его.

A List<Record> может толькосодержат Records (включая subRecords), A List<SubRecord> может содержать только SubRecords.

Но A List<SubRecord> не является List<Record>, если он не может содержать Records, и подклассы всегда должны делать то, что могут делать суперклассы.Это важно , поскольку наследование является спецификацией, если List<SubRecords> будет подклассом List<Record>, он должен содержать `но это не так.

A List<Record> иList<SubRecord> оба List<? extends Record>.Но в List<? extends Record> вы ничего не можете добавить, так как java не может знать, какого именно типа List является контейнером.Представьте, что вы могли бы, тогда вы могли бы иметь следующие утверждения:

List<? extends Record> l = l2;
l.add( new Record() );

Как мы только что видели, это возможно только для List<Record>, но не для любых List<Something that extends Record>, таких как List<SubRecord>.

С уважением, Стефан

2 голосов
/ 25 июня 2011

Просто чтобы объяснить, почему Java не допускает этого:

  • A List<Record> - это список, в который можно поместить любой объект, реализующий Record, и каждый полученный вами объект будет реализован Record.
  • A List<SubRecord> - это список, в который можно поместить любой объект, реализующий SubRecord, и каждый полученный вами объект будет реализовывать SubRecord.

Еслибыло бы разрешено просто использовать List<SubRecord> в качестве List<Record>, тогда было бы разрешено следующее:

List<SubRecord> subrecords = new ArrayList<SubRecord>();
List<Record> records = subrecords;
records.add(new Record()); // no problem here

SubRecord sr = subrecords.get(0); // bang!

Видите ли, это не будет безопасным для типов.List (или фактически любой объект параметризованного класса) не может безопасно изменять тип своего параметра.

В вашем случае я вижу следующие решения:

  • Использование List<Record>с самого начала.(Вы можете добавить SubRecord объектов к этому без проблем.)
    • в качестве варианта этого, вы можете использовать List<? super Subrecord> для метода, который добавляет вещи.List<Record> является подтипом этого.
  • скопировать список:

    List<Record> records = new ArrayList<Record>(subrecords);
    

Расширить немногопо варианту:

void addSubrecords(List<? super Subrecord> subrecords) {
    ...
}


List<Record> records = new ArrayList<Record>();
addSubrecords(records);
recordkeeper.records = records;
1 голос
/ 25 июня 2011

Вы не можете сделать это и быть в безопасности, потому что List<Interface> и List<SubInterface> - это разные типы в Java. Даже если вы можете добавить типы SubInterface в список Interface, вы не можете приравнять два списка к разным интерфейсам, даже если они являются sub / super интерфейсами друг друга.

Почему ты так хочешь сделать b = a? Вы просто хотите сохранить ссылку на список SubInterface?

В дополнение к этому, я предлагаю вам прочитать эту документацию на сайте оракула: http://download.oracle.com/javase/tutorial/java/generics/index.html Это очень хорошо объясняет и углубляется в дженерики.

0 голосов
/ 19 ноября 2014

ЭТО РАБОТА, НО ВЫ ДОЛЖНЫ БЫТЬ ПРЕДУПРЕЖДЕНЫ !!!!

@Override
// Patient is Interface
public List<Patient> getAllPatients() {

   // patientService.loadPatients() returns a list of subclasess of Interface Patient
   return (List<Patient>)(List<? extends Patient>)patientService.loadPatients(); 
}

Таким способом вы можете привести из Списка объектов в Список интерфейса. Но если вы получите этот список где-то в своем коде и если вы попытаетесь что-то добавить в этот список, что бы вы добавили? Интерфейс или подкласс этого интерфейса? Вы фактически теряете информацию о типе списка, потому что вы позволяете ему содержать интерфейс, так что вы можете добавить все, что реализует этот интерфейс, но список содержит только подклассы, и вы можете легко получить исключение приведения класса, если попытаетесь выполнить операции. как добавить или попасть в этот список с другим подклассом. Решение: Измените тип списка источников на list<Interface> вместо приведения, тогда вы можете идти:)

0 голосов
/ 27 июня 2011

Итак, довольно простое решение, найденное моим другом, было таким:

recordKeeper.myList = (List<Record>)(List<? extends Record>)records;

Это работает, как я понимаю, потому что это требует маленьких шагов. List<SubRecord> - это List<? extends Record>, а List<? extends Record> - это List<Record>. Это может быть не красиво, но, тем не менее, работает.

0 голосов
/ 25 июня 2011

Нет способа сделать это безопасным для типа.

A List<SubInterface> не может иметь произвольных Interface s, добавленных к нему.В конце концов, это список SubInterface с.

Если вы уверены, что это безопасно, даже если это не типобезопасно, вы можете сделать

@SuppressWarnings("unchecked")
void yourMethodName() {
  ...
  List<Interface> a = (List<Interface>) b;
  ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...