Ошибка: несовместимые типы: коллекция<DB_Account> не может быть преобразована в коллекцию<Account>
Следующее дает эту ошибку:
public class Account
{
...
}
public class DB_Account extends Account implements DBObject
{
...
}
public class Cache<E extends DBObject>
{
protected Map<Long,E> m_contents;
...
public Collection<E> values()
{
return m_contents.values();
}
}
public class DB_Imp implements Database
{
protected Cache<DB_Account> m_accounts;
...
@Override
public Collection<Account> getAccounts()
{
if (m_accounts.isValid())
/* Compiler gives error here */
return m_accounts.values();
...
}
}
В настоящее время я работал над ошибкой компилятора в DB_Imp
класс, добавив Collections.class.cast()
вокруг вызова accounts.values()
и добавил @SuppressWarnings
, Должен быть лучший способ. Другой способ - изменить класс Cache на:
@SuppressWarnings("unchecked")
public <T> Collection<T> values()
{
return Collections.class.cast(m_contents.values());
}
2 ответа
Проблема в том, что вы пытаетесь вернуть Collection<DB_Account>
как Collection<Account>
компилятор не позволит вам. Collection
не является ковариантным, так Collection<DB_Account>
не является подтипом Collection<Account>
,
Способ исправить это будет изменить метод values
в Database
чтобы:
Collection<? extends Account> values();
РЕДАКТИРОВАТЬ: Если вы не можете сделать это изменение, то я бы вместо этого сделал:
@Override
public Collection<Account> getAccounts() {
if (m_accounts.isValid())
return Collections.unmodifiableCollection(m_accounts.values());
...
}
Это создает неизменяемое представление о values()
с правильным типом параметра (Collection<Account>
). И это дает вам дополнительную безопасность (во время выполнения), что ваш Cache
не может быть изменено кодом клиента. values()
возвращает вид на Cache
карта, так что каждое изменение, внесенное в него кем-то, звонящим getAccounts()
будет отражать на Cache
, Это то, что вы обычно хотите избежать.
Сначала посмотрите на ошибку. Это говорит о том, что:
- За
Cache<DB_Account> m_accounts
вызов методаm_accounts.values()
возвращаетCollection<DB_Account>
, - Ands
getAccounts()
возвращаетсяCollection<Account>
- Но они не полиморфны:
Collection<Account>
не супертипCollection<DB_Account>
и поэтому компилятор не может конвертировать их.
Это абсолютно правильно и ожидаемо.
- Для супертипа
X
- И подтип
Y
Foo<Y>
не является подтипомFoo<X>
Это важнейшее понятие в дженериках Java, и его стоит понять. Вы должны прочитать Учебные руководства Java> Обобщения, Наследование и Подтипы.
Возможные решения
Трудно предложить решение, не зная точного контекста. Один из возможных подходов, если вы можете изменить Database
интерфейс, будет менять getAccounts
использовать подстановочный знак
public Collection<? extends Account> getAccounts();
Однако это только позволит вам получить предметы из возвращенных Collection
, не устанавливайте их в него (возможно, это то, что вы хотите). Чтобы понять, почему вы должны прочитать "Верхний ограниченный шаблон" и "Рекомендации по использованию шаблонов".