-1

I have the following code:

    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() + " ");
    }

My Questions:

  1. h.values() returns a Collection view of values in h. Since vals is an interface, how can we assign some values to an interface (they cannot be instantiated)? Where is the class that is implementing this interface? Where is the object of that class?

  2. Similar question for itr. We know vals.iterator() returns first element of the collection. How can we assign it to an interface instance?

sam
  • 95
  • 1
  • 1
  • 12
  • 3
    "*assign it to an interface instance*". Sounds like this is your problem. `Collection vals` does not declare an instance, it declares a *reference* to an implementation of `Collection`. And since we can *implement* interfaces, the problem does not appear. The implementation is (usually) internal to `HashMap`. In the case of OpenJDK, it's a [nested class](http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/HashMap.java#l956), implementing the `Collection` interface. – dhke Jun 01 '16 at 19:10
  • I just now tried printing `vals.getClass()` and it printed `class java.util.HashMap$Values`. So does that mean this class is implementing `Collection` interface? – sam Jun 01 '16 at 19:19
  • Yes, of course. By definition, if a variable is of type Collection, then it references an object whose class implements Collection. – JB Nizet Jun 01 '16 at 19:21
  • @sam: Exactly.For example, the OpenJDK implementation extends `AbstractCollection` which implements the `Collection` interface and hence can be assigned to any (reference) variable for that interface. – dhke Jun 01 '16 at 19:40

1 Answers1

2

The underlying principle that governs the answers to your questions is called the Liskov's Substitution Principle which applies in this case to assign the value that is of an instance of a given interface (Collection, Iterator) to a reference whose type is a class that implements that interface (e.g. AbstractCollection, Some anonymous class etc).

If you see the HashMap#values() method code, you'll see in Java 8 source code:

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(); }
    ...
}

Thus, you are being returned either

  1. an instance of Values class which extends an AbstractCollection which implements Collection, or
  2. an instance of a concrete subclass of AbstractCollection (see: values = new AbstractCollection<V>() at line 386 in AbstractMap.java in Java 8 source code.

Again, according to the LSP, this is all valid. To understand how all this is wired up, you'll need to grok the JDK code base.

More Answers

h.values() returns a Collection view of values in h. Since vals is an interface, how can we assign some values to an interface (they cannot be instantiated)?

To be precise, vals is not an interface, but an instance thereof. It's true that you can not instantiate an interface say Listener using the new operator like listener l = new Listener(), but according to the LSP, you can always instantiate a concrete implementation of the Listener interface and assign it to a variable whose type is Listener, like for example, Listener listener = new SerialListener();

Where is the class that is implementing this interface?

In this case, it is the Values class or AbstractCollection class as shown above.

Where is the object of that class?

In several cases, it is an instance of the anonymous inner class that is instantiated at the time of its definition.

We know vals.iterator() returns first element of the collection.

Not quite right. It returns an instance of a class that implements the Iterator interface. If and when you call the next() method on the returned object, you get the first element of the collection (assuming it is not empty).

How can we assign it to an interface instance?

The idea is the same. If the variable on the left hand side of an assignment statement (left of = sign) refers to an interface, then the right hand side can refer to a reference to an object that implements that interface, directly or indirectly (via inheritance hierarchy).

Community
  • 1
  • 1
Kedar Mhaswade
  • 4,535
  • 2
  • 25
  • 34