Несовместимые типы при использовании верхнего ограничивающего подстановочного знака
Я действительно запутался в том, как работают верхние ограниченные типы в генераторах Java.
Допустим, у меня есть
interface IModel<T>
interface I
class A implements I
class B implements I
class C implements I
Тогда у меня есть метод с параметром следующим образом
foo(IModel<Map<? extends I, Map<? extends I, List<? extends I>>>> dataModel)
Вызов этого метода как
IModel<Map<A, Map<B, List<C>>>> model = ...
foo(model)
Заканчивается ошибкой компиляции
Error:(112, 49) java: incompatible types: IModel<java.util.Map<A,java.util.Map<B,java.util.List<C>>>> cannot be converted to IModel<java.util.Map<? extends I,java.util.Map<? extends I,java.util.List<? extends I>>>>
Я читал документы о Java generics из Oracle web, пытаясь найти их в google, но там должно быть что-то, что я совершенно неправильно понял.
3 ответов:
Этот вопрос можно закоротить как почему
foo(IModel<List<? extends I>> dataModel)Не может принять аргумент, подобный
IModel<List<A>> modelОбъяснение
List<A>является подтипомList<? extends I>, так что все в порядке:public void bar(List<? extends I> list); List<A> listA; bar(listA);Но, это не делает
IModel<List<A>>подтипомIModel<List<? extends I>>, так же, какIModel<Dog>не является подтипомIModel<Animal>, Таким образом, код, который вы разместили, не может быть скомпилирован.Решение
Вы можете изменить его на:
foo(IModel<? extends Map<? extends I, ? extends Map<? extends I, ? extends List<? extends I>>>> dataModel)Или
<FIRST extends I, SECOND extends I, THIRD extends I> void foo(IModel<Map<FIRST, Map<SECOND, List<THIRD>>>> dataModel)Чтобы заставить его компилироваться.
Прежде всего, мне интересно, сколько усилий потребовалось бы вам (одному человеку), чтобы разобраться в коде, который должен быть в такой форме:
import java.util.List; import java.util.Map; interface IModel<T> {} interface I {} class A implements I {} class B implements I {} class C implements I {} public class UpperBounds { public static void main(String[] args) { IModel<Map<A, Map<B, List<C>>>> model = null; foo(model); } static void foo(IModel<Map<? extends I, Map<? extends I, List<? extends I>>>> dataModel) { } }Вместо того, чтобы позволить сотням людей (которые хотят помочь вам) сделать это самостоятельно, чтобы иметь что-то, что они могут скомпилировать и посмотреть в своей IDE. Я имею в виду, что это не так уж сложно.
Как говорится: технически, вы пропускаете здесь еще несколько
extendsпунктов. Это компилирует отлично:import java.util.List; import java.util.Map; interface IModel<T> {} interface I {} class A implements I {} class B implements I {} class C implements I {} public class UpperBounds { public static void main(String[] args) { IModel<Map<A, Map<B, List<C>>>> model = null; foo(model); } static void foo(IModel<? extends Map<? extends I, ? extends Map<? extends I, ? extends List<? extends I>>>> dataModel) { } }Но вы должны
Не
Реализуйте это так. Это непонятно. Каким бы ни был этот параметр
dataModel, Вы должны рассмотреть возможность создания надлежащей структуры данных для этого, а не передавать такой беспорядок глубоко вложенных универсальных карт.
Причина, по которой исходная версия не компилировалась, уже упоминалась в других ответах. И это можно сделать более ясным, показав пример с использованием намного более простого вызова метода. Считать этот пример:interface IModel<T> {} interface I {} class A implements I {} class B implements I {} class C implements I {} public class UpperBounds { public static void main(String[] args) { List<List<A>> lists = null; exampleA(lists); // Error exampleB(lists); // Works! } static void exampleA(List<List<? extends I>> lists) { } static void exampleB(List<? extends List<? extends I>> lists) { } }Метод
exampleAне может принять данный список, тогда как методexampleBможет принять его.Детали хорошо объяснены в какие отношения супер-подтипов существуют среди экземпляров общих типов? из часто задаваемых вопросов по дженерикам Анжелики Лангер.
Интуитивно ключевым моментом является то, что типList<A>является подтипомList<? extends I>. Но разрешение методу принимать только aList<List<? extends I>>не позволяет передавать список, элементы которого являются подтипами изList<? extends I>. Чтобы принять подтипы, вы должны использовать? extends.(Это можно было бы даже упростить дальше: когда метод принимает
List<Number>, то вы не можете передать вList<Integer>. Но это не прояснило бы здесь смыслList<A>быть подтипомList<? extends I>)
Имея метод
method1(Map<I> aMap>)и будучи классом, реализующим I, вы не можете вызвать метод с помощьюMap<A>, и это не случайно.Имея метод:
public static void foo2(IModel<I> dataModel) { System.out.println("Fooing 2"); }Представьте себе такой код:
IModel<A> simpleModel = new IModel<A>() {}; foo2(simpleModel);Это не должно работать, потому что вы предоставляете более конкретный тип методу, который требует универсального типа. Теперь представьте, что foo2 делает следующее:
public static void foo2(IModel<I> dataModel) { dataModel = new IModel<B>() {}; System.out.println("Fooing 2 after we change the instance"); }Здесь вы попытаетесь установить IModel на IModel, который является допустимым-потому что B расширяет I, но если бы вы могли вызвать это метод с IModel это не будет работать
Создайте свою модель следующим образом:
IModel<Map<I, Map<I, List<I>>>> model = ...И в соответствующие карты и списки добавить объекты типа A, B и C, которые будут действительны, а затем вызвать функцию foo (model)
Comments