Java Generics: Description and Methods

Since its inception, the Java language has undergone a lot of changes, which undoubtedly brought positive aspects to its functionality. One of these significant changes is the introduction of Java Generic or generalization. This functionality has made the language not only more flexible and versatile, but also much safer in terms of data type casting.

java generics description

The fact is that before the introduction of generics, generalized code in Java could be created only using links of type Object. Such links can be assigned to any object. After all, all classes in Java are implicit descendants of the Object class. However, this approach is a potential source of many errors related to type safety when explicitly converting an object from Object to the target type. When using generalizations, all reductions are performed implicitly and automatically, which eliminates even the potential for errors.

Java Generics: Description and Example

Let's look at a simple example of applying generalization to a regular class in the figure below. And then we proceed to a detailed examination of all the intricacies and nuances of Java Generic.

generic class java

Pay attention to how the Pair class declaration occurs. Immediately after the class name, angle brackets open in which the letter T is indicated. It is a kind of placeholder, which will be replaced by a specific type during the creation of an instance of this class. It looks like this: Pair <Integer> obj = new Pair <Integer> (). It should be noted that instead of T you can specify any letter, but, as a rule, use T, V or E.

Note: starting from the eighth version of Java, specifying the target type when declaring a link, angle brackets in the constructor can be left empty. So the above example can be rewritten as follows: Pair <Integer> obj = new Pair <> ().

When a class is declared in this way, further in its body, instead of specific types of fields, links, and objects returned by methods, this letter can be used. Since T when creating an object of the class is replaced by a specific type, the fields first and second in this case will be of type Integer.

Following the logic, the arguments firstItem and secondItem passed to the corresponding constructor must also be of type Integer or its subclass. If you try to pass a data type that is different from what was specified when creating the object, the compiler will not miss this error. So, a constructor with arguments when creating an object will look like this: Pair <Integer> obj = new Pair <> (new Integer (1), new Integer (2)). The same applies to the arguments to the setFirst and setSecond methods. And as you probably already guessed, the getFirst and getSecond methods will return values ​​of type Integer.

A generic class with several type parameters

In generalized classes, you can also declare several type parameters that are specified in angle brackets, separated by commas. The Pair class, processed for such a case, is shown in the figure below.

java generic

As you can see, when creating an instance of such a class, the same number of types as the parameters should be indicated in angle brackets. If you are familiar with this kind of data structure like Map, then you may have noticed that the exact same principle is used there. There, the first argument determines the type of key, and the second - the type of value. It should be noted that the types of arguments passed when creating the object may coincide. So, the following declaration of an instance of the Pair class is absolutely correct: Pair <String, String> obj.

Some features of generalizations

Before going further, it should be noted that the Java compiler does not create any different versions of the Pair class. In fact, during compilation, all information about the generic type is deleted. Instead, the appropriate types are cast, creating a special version of the Pair class. However, in the program itself, there is still a single generalized version of this class. This process is called in Java Generic type cleaning.

Note an important point. References to different versions of the same java generic class cannot point to the same object. That is, let's say we have two links: Pair <Integer> obj1 and Pair <Double> obj2. Therefore, an error will occur in the line obj1 = obj2. Although both variables are of type Pair <T>, the objects to which they refer are different. This is a prime example of type safety in Java Generic.

Restrictions on Generalized Classes

It is important to know that generalizations can be applied only to reference types, that is, the argument passed to the generic class java parameter must be a class type. Simple types, such as double or long, cannot be passed. In other words, the following line of declaration of the Pair class is not valid: Pair <int> obj. Nevertheless, this restriction does not pose a serious problem, since in Java for each primitive type there is a corresponding wrapper class. Strictly speaking, if in the Pair class you want to encapsulate an integer and boolean value, auto-packing will do everything for you: Pair <Integer, Boolean> obj = new Pair <> (25, true).

Another major limitation is the inability to instantiate a type parameter. So, the following line will cause a compilation error: T first = new T (). This is obvious, because you do not know in advance whether a full-fledged class or an abstract or an interface will be passed as an argument. The same goes for creating arrays.

Limited types

Quite often, situations arise when it is necessary to limit the list of types that can be passed as an argument to the java generic class. Suppose that in our Pair class we want to encapsulate exclusively numeric values ​​for further mathematical operations on them. To do this, we need to set the upper bound of the type parameter. This is accomplished by declaring a superclass inherited by all arguments passed in angle brackets. It will look like this: class Pair <T extends Number>. In this way, the compiler learns that instead of the parameter T, you can substitute either the Number class or one of its subclasses.

This is a common technique. Such restrictions are often used to ensure compatibility of type parameters in the same class. Consider an example on our Pair class: class Pair <T, V extends T>. Here we inform the compiler that type T can be arbitrary, and type V must be either type T or one of its subclasses.

The restriction β€œfrom below” occurs in exactly the same way, but instead of the word extends, the word super is written. That is, the declaration of class Pair <T super ArrayList> indicates that instead of T, either ArrayList or any class or interface that it inherits can be substituted.

Generic Java Methods and Constructors

In Java, generalizations can be applied not only to classes, but also to methods. So, a generalized method can be declared in a regular class.

generic java methods

As you can see in the figure above, there is nothing complicated in declaring a generic method. It is enough to put angle brackets in front of the type returned by the method and specify the type parameters in them.

In the case of the constructor, everything is done in the same way:

java generic type cleanup

In this case, angle brackets are preceded by the name of the constructor, since it does not return any value. The result of the work of both programs will be:

Integer

String

Source: https://habr.com/ru/post/K3509/


All Articles