我有一个HashMap,其值是ArrayLists,我正在尝试编写一个函数来接受这些HashMaps的泛型实例
HashMap<String, ArrayList<Integer>> myMap = new HashMap<String, ArrayList<Integer>>();
public static void foo(HashMap<?, ArrayList<?>> a) {}
public static void bar(HashMap<?, ? extends ArrayList<?>> a) {}
// Compilation Failure!
foo(myMap);
// This works, but why do I need ? extends ArrayList
bar(myMap)
错误消息是
foo(HashMap<?,ArrayList<?>>)
类型中的方法Example
不适用 对于参数(HashMap<String,ArrayList<Integer>>
)。
为什么我需要为extends ArrayList设置通配符?
我认为通过ArrayList<?>
(没有? extends
),我可以将函数限制为只有带有ArrayList值的HashMaps。
我也知道以下通用方法有效:
public static <K,V> void printer(HashMap<K, ArrayList<V>> list) { }
我认为ArrayList<?>
的作用如何。有人能解释这里的细微之处吗?
答案 0 :(得分:6)
通配符语法是故意设计的(误)引导人们相信它匹配任何类型。这适用于简单的情况
List<?> <= List<String> // OK, right is subtype of left
但请记住,它只适用于第一级,而不是更深层次
List<List<?> <= List<List<String>> // FAIL
这很好
List<? extends List<?>> <= List<List<String>>
因为新的第1级?
。
如果S
是T
的子类型,则G<S>
是G<? extends T>
的子类型。如果是S=List<String>
,T=List<?>
。
答案 1 :(得分:1)
类型HashMap<?, ArrayList<?>>
表示地图,它将某些未知(固定)类型的键映射到列表,每个列表都包含某些未知固定类型的元素。
这种地图的具体实例将是,例如:
new HashMap<String, ArrayList<?>>();
这是一个地图,其中我们可以添加任意ArrayLists 作为值,对于我们从中获取的值(列表),我们对参数类型一无所知。
类型HashMap<String, ArrayList<Integer>>
表示从字符串到整数列表的映射。这是一个地图,我们只能将整数的ArrayLists作为值添加,不是任意的ArraysLists 。因此,它不能是类型2的子类型(因为它允许少于它)。
(类型1是超类型2,也可以使密钥类型未知。)
类型HashMap<String, ? extends ArrayList<?>>
表示从字符串到某种未知类型的映射,只知道它是ArrayList<?>
的子类型。在这样的映射中,我们不能插入任何东西(作为值),但是我们得到的所有值都保证是ArrayList<?>
类型,即未知事物的arraylist(可能是每个列表的另一个东西) )。我们可以插入新的String键,但只能使用null
值。
类型2和类型3都是类型4的子类型(因为ArrayList<?>
和ArrayList<Integer>
都满足extends ArrayList<?>
条件),但彼此不符合。
HashMap<?, ? extends ArrayList<?>>
就像4,但我们也不知道密钥类型。这意味着我们无法对地图做任何事情,除了迭代它(或查找键的值 - get()
采用Object
参数,而不是K
)并删除内容。对于我们检索的值,我们只能迭代和删除东西,而不是插入甚至重新排序任何东西。 (即类型4是类型5的子类型。)
类型为变量HashMap<K, ArrayList<V>>
的方法参数类型<K, V>
表示从某种类型K到某些类型V的列表的映射,其中K和V由调用者决定方法。类型3显然适合这种情况(使用<String, Integer>
),因此您可以调用此方法。您无法将新密钥放入地图中,因为您的方法不知道K
是什么,但您可以将现有密钥映射到新值。您可以使用new ArrayList<V>()
(并将这些元素作为值放入地图中),但您只能将现有列表中的现有V
对象放入这些列表中。比5型还要多得多。
请注意:您应该使用更常规的类型Map<K,V>
和List<E>
而不是HashMap<K,V>
和ArrayList<E>
作为您的方法,因为大多数情况下该方法都会适用于任何类型的地图/列表,并不关心具体实施。你也可以使用
Map<String, List<Integer>> myMap = new HashMap<String, List<Integer>>();
声明并初始化您的变量。 (您仍然可以将ArrayList<Integer>
个对象作为值。)
答案 2 :(得分:0)
这很简单。即使HashMap<K, V>
是HashMap<K, W>
的子类型,V
也不是W
的子类型。 (你已经知道了,对吗?)在这里,V
是ArrayList<Integer>
,W
是ArrayList<?>
。 (重要的是它们是不同的。)是的,ArrayList<Integer>
是ArrayList<?>
的子类型,但这就是我们之前讨论的内容,即它是无关紧要的。但是,由于通配符,HashMap<K, V>
是<{1}}的子类型。