What class instance does Map.of() return? - java

I'm currently learning about collections and I noticed that the factory method Map.of returns an object of type Map<>. However since Map is only an interface what class does the Map class reference actually point to?

It is not guaranteed to return an instance of any specific class, so your code should not depend on that. However, for the sake of learning, OpenJDK 11 returns an instance of ImmutableCollections.Map1, one of several lightweight special-purpose classes written specifically for the Map.of overloads.

It won't be the same depending on the Map size and you should not rely on the value returned as per the answer by #chrylis -cautiouslyoptimistic-.
If you are interested on the value of any Map instance your code uses just print map.getClass(), here are a few examples:
System.out.println(Map.of().getClass());
System.out.println(Map.of(1,2).getClass());
System.out.println(Map.of(1,2,3,4,5,6).getClass());
Which (in JDK17) prints:
class java.util.ImmutableCollections$MapN
class java.util.ImmutableCollections$Map1
class java.util.ImmutableCollections$MapN

It returns an Immutable Map from the ImmutableCollections class.
This class is not part of the public API, but it extends AbstractMap which supplies implementations for all of the basic methods needed for a Map.
The important takeaway is that the Map returned by Map.of() is immutable so you can't add to or change it after it is created. Immutable collections are more secure, are thread safe, and can be more efficient.

Related

Static Factory method Advantages

I was reading effective java and one advantage of static factory methods written is that they can return an object of any sub-type of return type.
I understood the way we can implement this as mentioned in following link with example.
https://www.slideshare.net/mysky14/java-static-factory-methods
But in the book an example of Collections API is given that has static factory methods in java.util.Collections utility class and it is written that "Collections API is much smaller than it would have been had it exported 32 separate public classes".
It is also mentioned that in this manner, API can return objects without their classes to be public and this results in very compact API.
I want to know how the API size is reduced by implementing this method and not having separate public classes.
I want to know how the API size is reduced by implementing this method and not having separate public classes.
Let's use the same concrete example used in the book: java.util.EnumSet has static factories that return one of two implementations: RegularEnumSet or JumboEnumSet. These implementations have their own complexities, but are effectively hidden to the clients of Collections. In theory, the factories could use other implementations in the future, and the clients of them would not be affected.
If you visualized this in a class diagram, the factory methods (e.g., of(), as opposed to a constructor) return an abstract type EnumSet, which hides the details of the implementations. Abstract (or Interface) types effectively abstract (simplify) the API.
What's more, the implementations are actually package private, meaning they're declared without a public keyword. This means that only classes in the same package can see them, so it prevents having Client depend on them. This is a great example of information hiding, which allows API developers to simplify their API and also to change the hidden parts later without breaking the code.
Another example that comes to mind where factory methods can simplify an API are the concrete iterators in Collections. In this case, it's a factory method that is not static, e.g., ArrayList.iterator(), that returns a concrete iterator for ArrayLists. The name of this class is even less "known" than the EnumSet implementations.
In general having static factory method would take out your object instantiation logic out of your class. Suppose based on certain logic, you need to return different subclass objects. This would result in if-else logic in your class method whichever is responsible for appropriate object instantiation. Moving this out to static factory method would result in cleaner class design which would be easier to test and closer to "Closed to modification" principle

Encapsulation: why the name keySet in java

In java to get all the keys in a map we can use the method keySet. But I was wondering why the method name is not just keys? isn't the name Set leaks details about the implementation?
As per my understanding Java is statically typed language and having types in names makes no sense at all. The calling code anyways must have the correct interface type. If we assume that this strategy is correct then every method must have types prefixed to them which doesn't makes any sense. I think #JBNizet stated correctly in his comment the reason behind the choice.
isn't the name Set leaks details about the implementation?
1) Set is an interface.
If for example, the method were named keyHashSet(), it would not be a good idea
as HashSet is an implementation of Set.
But keySet() is fine because having as return type an interface that defines a specific contract and using a name that conveys this specific contract in the method name is a valid way to program by interface and to be able to return any implementation of Set in a clean way.
2) Besides choosing in the Map interface the keys() method name rather than keySet() is not possible either.
As #JB Nizet suggests in its comment, a keys() method is already defined in a JDK collection class : Enumeration<K> keys().
It is declared in the Dictionary class that HashTable inherits from :
public class Hashtable<K,V> extends Dictionary<K,V>...{
But this method : Enumeration<K> keys(); doesn't return a Set.
So HashTable that is both a Dictionary and a Map :
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>,
has to implement one method to return the keys in an Enumeration form but also another one to return them in a Set form.
A distinct naming is so required to distinguish them
You may also note that Set<Map.Entry<K, V>> entrySet() also defined in the Map interface follows the same naming logic while no other hierarchy of HashTable defines a entries() method.
It is probably to be consistent.
I was wondering why the method name is not just keys
Like so many things in modern Java, backwards compatibility got in the way a bit: keys() was already taken (by java.util.Hashtable.keys(), and that class should still be able to implement Map), so they had to choose something else.
Doesn't the name Set leaks details about the implementation ?
No, it does not. The Map interface already specifies that there cannot be duplicate keys. So the collection of keys is a Set by definition. And Set is still an interface for which there can be different implementations.
isn't the name Set leaks details about the implementation?
The declaration Set<K> keySet() doesn't tell us any more about the
implementation than Set<K> keys() would. In both cases, we know that the method returns a Set. Set is an interface, not a concrete class.
Whether it's a good idea to include that in the name is a matter of style (personally, no, I don't think it is; but reasonable people can differ). But doing so doesn't tell us anything about the implementation that we wouldn't know anyway.

Why does the Collection interface have equals() and hashCode()?

Why does the Collection interface have equals(Object o) and hashCode(), given that any implementation will have those by default (inherited from Object) ?
From the Collection JavaDoc:
While
the Collection interface adds no stipulations to the general contract
for the Object.equals, programmers who implement the Collection
interface "directly" (in other words, create a class that is a
Collection but is not a Set or a List) must exercise care if they
choose to override the Object.equals. It is not necessary to do so,
and the simplest course of action is to rely on Object's
implementation, but the implementor may wish to implement a "value
comparison" in place of the default "reference comparison." (The List
and Set interfaces mandate such value comparisons.)
The general contract for the Object.equals method states that equals
must be symmetric (in other words, a.equals(b) if and only if
b.equals(a)). The contracts for List.equals and Set.equals state that
lists are only equal to other lists, and sets to other sets. Thus, a
custom equals method for a collection class that implements neither
the List nor Set interface must return false when this collection is
compared to any list or set. (By the same logic, it is not possible to
write a class that correctly implements both the Set and List
interfaces.)
and
While the Collection interface adds no stipulations to the general contract for the Object.hashCode method, programmers should take note that any class that overrides the Object.equals method must also override the Object.hashCode method in order to satisfy the general contract for the Object.hashCode method. In particular, c1.equals(c2) implies that c1.hashCode()==c2.hashCode().
To answer your specific question: why does it have these methods? It's done simply for convenience to be able to include Java Docs giving hints as to what implementers should do with these methods (e.g. comparing equality of values rather than references).
To add to the other great answers. In the Collections interface, the equals method is defined in that interface to make some decisions in the way equaling two instances of collection should work. From the JAVA 8 documentation:
More generally, implementations of the various Collections Framework
interfaces are free to take advantage of the specified behavior of
underlying Object methods wherever the implementor deems it
appropriate.
So you don’t add methods from the Object class for any other reason that giving more definitiveness to the java doc. This is the reason why you don’t count those methods in the abstract methods in the abstract methods of an interface.
Moreover, in JAVA 8, along the same line of reasoning, default methods from the Object class are not allowed and will generate a compile error. I believe it’s was done to prevent this type of confusion. So if you try to create a default method called hashCode(), for example, it will not compile.
Here is a more in-depth explanation for this behavior in JAVA 8 from the Lambda FAQ:
An interface cannot provide a default implementation for any of the
methods of the Object class. This is a consequence of the “class wins”
rule for method resolution: a method found on the superclass chain
always takes precedence over any default methods that appear in any
superinterface. In particular, this means one cannot provide a default
implementation for equals, hashCode, or toString from within an
interface.
This seems odd at first, given that some interfaces actually define
their equals behavior in documentation. The List interface is an
example. So, why not allow this?
One reason is that it would become more difficult to reason about when
a default method is invoked. The current rules are simple: if a class
implements a method, that always wins over a default implementation.
Since all instances of interfaces are subclasses of Object, all
instances of interfaces have non-default implementations of equals,
hashCode, and toString already. Therefore, a default version of these
on an interface is always useless, and it may as well not compile.
Another reason is that providing default implementations of these
methods in an interface is most likely misguided. These methods
perform computations over the object’s state, but the interface, in
general, has no access to state; only the implementing class has
access to this state. Therefore, the class itself should provide the
implementations, and default methods are unlikely to be useful.
Just to add to the great answers above, it makes sense to have the 'equals' or `hashCode' methods in this scenario:
Collection<Whatever> list1 = getArrayList();
Collection<Whatever> list2 = getAnotherArrayList();
if(list1.equals(list2)){
// do something
}
In the absence of the equals method in the interface, we'll be forced to use concrete types, which is generally not a good practice :
ArrayList<Whatever> list1 = getArrayList();
ArrayList<Whatever> list2 = getAnotherArrayList();
if(list1.equals(list2)){
// do something
}

How can ImmutableMap builder create instance of abstract class?

I am learning to use guava library and referred to this I see use of builder to construct an instance of immutable map. How is the builder constructor able to create an abstract class instance?
static final ImmutableMap<String, Integer> WORD_TO_INT =
new ImmutableMap.Builder<String, Integer>()
.put("one", 1)
.put("two", 2)
.put("three", 3)
.build();
I does not, the Builder creates an implementation of an ImmutableMap (a class that extends the ImmutableMap).
To understand clearly, start here before working with Guava.
Update: see comment by #Louis Wasserman. Indeed an essential comment.
ImmutableMap (Guava: Google Core Libraries for Java 21.0-SNAPSHOT API) states that it is "a Map whose contents will never change, with many other important properties detailed at ImmutableCollection".
The "other important properties detailed at ImmutableCollection" include the following guarantees:
Each makes the following guarantees:
Shallow immutability. Elements can never be added, removed or replaced in this collection. This is a stronger guarantee than that of Collections.unmodifiableCollection(java.util.Collection<? extends T>), whose contents change whenever the wrapped collection is modified.
Null-hostility. This collection will never contain a null element.
Deterministic iteration. The iteration order is always well-defined, depending on how the collection was created (see the appropriate factory method for details). View collections such as Multiset.elementSet() iterate in the same order as the parent, except as noted.
Thread safety. It is safe to access this collection concurrently from multiple threads.
Integrity. This type cannot be subclassed outside this package (which would allow these guarantees to be violated).
The last guarantee, integrety, alludes to the fact that internally Guava has concrete implementations (non-abstract) of ImmutableMap and other immutable objects which is what is actually returned by these builders.
Furthermore, the source is open; you can go find out for yourself how the builder is able to do it (e.g. you might start here).
It is not the constructor of the builder class that returns an instance of an immutable map.
You are first creating the builder by calling new ImmutableMap.Builder<String, Integer>() and then you are calling methods in a chain on this ImmutableMap.Builder instance - three times the put method and then the build method.
The build method is what is called last, and that is what creates and returns the instance of ImmutableMap.
The "trick" here is that the put method of ImmutableMap.Builder returns the builder itself (it has a statement return this; at the end) so that you can chain method calls like this.
And, indeed, the build method returns an instance of a subclass of ImmutableMap because class ImmutableMap is abstract, so it cannot be directly instantiated.

What's the difference between these two java variable declarations?

public class SomeClass {
private HashSet<SomeObject> contents = new HashSet<SomeObject>();
private Set<SomeObject> contents2 = new HashSet<SomeObject>();
}
What's the difference? In the end they are both a HashSet isn't it? The second one looks just wrong to me, but I have seen it frequently used, accepted and working.
Set is an interface, and HashSet is a class that implements the Set interface.
Declaring the variable as type HashSet means that no other implementation of Set may be used. You may want this if you need specific functionality of HashSet.
If you do not need any specific functionality from HashSet, it is better to declare the variable as type Set. This leaves the exact implementation open to change later. You may find that for the data you are using, a different implementation works better. By using the interface, you can make this change later if needed.
You can see more details here: When should I use an interface in java?
Set is a collection interface that HashSet implements.
The second option is usually the ideal choice as it's more generic.
Since the HashSet class implements the Set interface, its legal to assign a HashSet to a Set variable. You could not go the other way however (assign a Set to a more specific HashSet variable).
Set is an interface that HashSet implements, so if you do this:
Set<E> mySet = new HashSet<E>();
You will still have access to the functionality of HashSet, but you also have the flexibility to replace the concrete instance with an instance of another Set class in the future, such as LinkedHashSet or TreeSet, or another implementation.
The first method uses a concrete class, allowing you to replace the class with an instance of itself or a subclass, but with less flexibility. For example, TreeSet could not be used if your variable type was HashSet.
This is Item 52 from Joshua Bloch's Effective Java, 2nd Edition.
Refer to Objects by their interfaces
... You should favor the use of interfaces rather than classes to refer to objects. If appropriate interface types exist, then parameters, return values, variables, and fields should all be declared using interface types. The only time you really need to refer to an object's class is when you're creating it with a constructor...
// Usually Good - uses interface as type
List<T> tlist = new Vector<T>();
// Typically Bad - uses concrete class as type!
Vector<T> vec = new Vector<T>();
This practice does carry some caveats - if the implementation you want has special behavior not guaranteed by the generic interface, then you have to document your requirements accordingly.
For example, Vector<T> is synchronized, whereas ArrayList<T> (also an implementer of List<T>) does not, so if you required synchronized containers in your design (or not), you would need to document that.
One thing worth to mention, is that interface vs. concrete class rule is most important for types exposed in API, eg. method parameter or return type. For private fields and variables it only ensures you aren't using any methods from concrete implementation (i.e. HashSet), but then it's private, so doesn't really matter.
Another thing is that adding another type reference will slightly increase size of your compiled class. Most people won't care, but these things adds up.

Categories