我有以下代码:
HashMap<Integer, String> h = new HashMap<Integer, String>();
h.put(1, "a");
h.put(2, "b");
h.put(3, "c");
h.put(4, "d");
System.out.println(h); //{1=a, 2=b, 3=c, 4=d}
Collection<String> vals = h.values();
System.out.println(vals); //[a, b, c, d]
Iterator<String> itr = vals.iterator();
while (itr.hasNext()) //a b c d
{
System.out.print(itr.next() + " ");
}
我的问题:
h.values()
返回h
中值的集合视图。既然vals
是一个接口,我们如何为接口分配一些值(它们不能被实例化)?实现此接口的类在哪里?该类的对象在哪里?itr
类似的问题.我们知道vals.iterator()
返回集合的第一个元素。我们如何将其分配给接口实例?
控制问题的答案的基本原则称为 Liskov 替换原则,在这种情况下,它适用于将给定接口(Collection
、Iterator
)的实例的值分配给一个引用,其类型是实现该接口的类(例如AbstractCollection
,一些匿名类等)。
如果您看到HashMap#values()
方法代码,您将在 Java 8 源代码中看到:
public Collection<V> values() {
Collection<V> vs;
return (vs = values) == null ? (values = new Values()) : vs;
}
final class Values extends AbstractCollection<V> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
...
}
因此,您要么被退回
Values
类的实例,它扩展了实现Collection
的AbstractCollection
,或- Java 8 源代码中
AbstractCollection
的具体子类的实例(请参阅:values = new AbstractCollection<V>()
在AbstractMap.java
的第386
行。
同样,根据LSP的说法,这一切都是有效的。要了解这一切是如何连接的,您需要了解JDK代码库。
更多答案
h.values() 返回 h 中值的集合视图。由于 vals 是一个接口,我们如何为接口分配一些值(它们不能实例化)?
准确地说,vals
不是一个接口,而是它的实例。确实,您不能像listener l = new Listener()
这样的new
运算符实例化接口,例如Listener
,但根据 LSP,您始终可以实例化Listener
接口的具体实现并将其分配给类型为Listener
的变量,例如,Listener listener = new SerialListener();
实现此接口的类在哪里?
在本例中,它是Values
类或AbstractCollection
类,如上所示。
该类的对象在哪里?
在某些情况下,它是在其定义时实例化的匿名内部类的实例。
我们知道 vals.iterator() 返回集合的第一个元素。
不太对劲。它返回实现Iterator
接口的类的实例。如果对返回的对象调用next()
方法,则会获得集合的第一个元素(假设它不为空)。
我们如何将其分配给接口实例?
想法是一样的。如果赋值语句左侧(符号左侧=
变量)引用接口,则右侧可以直接或间接(通过继承层次结构)引用实现该接口的对象的引用。