What is the difference in creating and defining these classes, for example:
HashMap<Object, Object> mapa = new HashMap<Object, Object>();
or this way:
Map<Object, Object> mapa = new HashMap<Object, Object>();
or this way:
Map<Object, Object> mapa = new HashMap<>();
Also:
HashSet<Object, Object> mapa = new HashSet<Object, Object>();
or this way:
Set<Object, Object> mapa = new HashSet<Object, Object>();
or this way:
Set<Object, Object> set = new HashSet<>();
Also:
ArrayList<Object> lista = new ArrayList<Object>();
or this way:
List<Object> lista = new ArrayList<Object>();
...and so on with collections... What is the best class definition? or because it is more preferable or more recommendable depending on what I want to use it for... or is there more correct ways?
Before we start: Actually, your examples should be
or, as of Java 7, also works
Otherwise, you are assigning an expression that does not have generics defined (an unbound value ) to one that does, and it shows you a warning.
On the crux of the question, it has nothing to do with generics per se. It could also be done before generics were defined (in Java 5), and of course it still can be done with any class as long as the variable it is assigned to is a supertype of the assigned expression. Trivial example
Object obj = new Integer(1)
.What you do here is define the type of a variable. The general rule is that you should choose as the class of the reference the most generic class (= higher in the hierarchy) that you can work with.
For example, you want to have a reference that behaves like a list (you can add elements and retrieve them based on their position). Do
doesn't make much sense, because the class
Object
doesn't define a method to add objects to the collection; if you do youmiLista.add("Hola");
have a compile error.Thus, it is more practical for you to define the variable/reference as
List miLista
, since that way you can already invoke the methodadd
of the implementation of yourList
choice.And why not
ArrayList miLista<String> = new ArrayList<>();
? Well, because you unnecessarily restrict the type of values you can assign. If all you're going to use are methods defined on the interfaceList
, using a referenceArrayList
will unnecessarily restrict your code to that implementation. A very clear example is in the parameters of the methods, imagine that you have a method to duplicate the last element of the list:The first version can be used with any implementation of
List
, while the second version can only be used if you have oneArrayList
or create oneArrayList
from your listIn the first construction you are declaring a map object of type
HashMap
and defining map as an object that is an instance of the classHashMap
. In the second construction you are declaring an object of typeMap
and defining map as an object that is an instance of the classHashMap
. The second construct follows a pattern known as 'programming against the interface':Map
it is an interface , therefore it defines a set of methods that classes that implement this interface must complete. The classHashMap
implements the interfaceMap
, and therefore agrees to implement all the methods that the interfaceMap
defines.By virtue of inheritance in Java, a class
A
that extends or implements a class or interfaceB
implies that an object of the classA
is an object of the classB
. Let's look at an example:The interface
Animal
defines three methods that anything that can be said to be an animal could do: feed, reproduce, or move. Any class that implements the interfaceAnimal
must implement these methods, for example a dog:or a cat:
Both classes implement the interface
Animal
because they fill in the definitions of the methods that the interface forces them to have. It is clear that each of these implements the methods in a different way (because not all animals behave in the same way). Thanks to this it is possible to do things like:By doing this we are programming against the interface, both
snoopy
asbolaDeNieve
they are defined as interfaces but declared as their specialized classes. On the other hand it is obvious that we can also do:but we can't do
because we cannot declare objects of type interface. What is the advantage of programming against interface? a well-known one is the use of polymorphism, suppose we have a veterinarian that treats dogs and cats and we have several animals:
When the animals arrive at the vet, they are anesthetized to be able to operate on them, by virtue of polymorphism we can create a method called
anestesiarPerro
that receives a dog and anesthetizes it:Or anesthetize a cat:
Since the method is generic, we can use polymorphism to receive the interface
Animal
and call the methodanestesiar
with an object of any subtype ofAnimal
both present (Cat
,Dog
) and future (tomorrow the vet can anesthetize birdsBird
):There are many more applications that benefit from programming against the interface, for example making use of a design pattern known as inversion of control and allowing the Liskov substitution principle to be applied . Going back to the aforementioned example, defining
mapa
asMap
and not asHashMap
allows this object to be used in methods that receive the most general class possible (interface), guaranteeing flexibility in the code.In the same way:
As much
List
asSet
they are interfaces andArrayList
andHashSet
implementations of these interfaces. Since the most general (interfaces over implementations) is preferred, it is always advisable to define these objects with the type of the interface and declare them with the implementation subtype of the interface.On the other hand:
is identical to
since Java 7 the diamond operator (<>) is a language feature that allows the compiler to guess what data type the generic object should have on the right side of the
a partir de la información del tipo de dato genérico a la izquierda de la asignación. Si te das cuenta, declaras un objeto
Mapcuyas llaves son de tipo
Objecty valores de tipo
Object, por lo tanto, al usar el operador
<>` assignment without any data type generic, the compiler assumes that the generic data type is the same as the one indicated in the variable definition, an example:create an array of
Dog
. Note the redundancy when declaring the generic data typeDog
inList<Dog>
and inArrayList<Dog
. Using the diamond operator is equivalent:Since from it
List<Dog>
is possible to intuit that the generic data type ofArrayList<>
isArrayList<Dog>
. I point out again that this is only possible since Java 7. In Java 5 and 6 it is mandatory to write the data type of the generic both to the left and to the right of the declaration or assignment of a generic. This operator ultimately only works as syntactic sugar to reduce the verbosity of the language.