I've been researching generics again for the past few days, so after giving a brief overview of generics, I decided to summarize the following two contents as a memo.
--Generics scope --Talk about type variables
Anyway, I would like to reorganize the merits of using Generics. It ** will notify you at compile time if you try to insert an object of the wrong type ** (quoted from Effective Java 3rd Edition Chapter 5 Generics). As a result, similar processing can be realized for various types within the range in which type safety can be guaranteed. Now, I would like to show a concrete example in code. Here, I would like to recognize the goodness of writing in generics by comparing arrays and generics. Write the definition of the list, add to the list, and retrieve it without using generics as follows.
ArrayList list = new ArrayList();
list.add("hoge");
String s = (String) list.get(0);
Since it is extracted as Object type, it is necessary to explicitly cast it, and if there is an error (such as assigning with int and extracting with String), you will not notice the error unless you move it. ← This is very painful. Write the definition of the list using generics, the addition to the list, and the retrieval as follows.
ArrayList<String> list = new ArrayList<String>();
list.add("hoge");
String s = list.get(0);
I get an error when compiling. (You can notice the mistake before executing it ← This is very nice)
Generics has two scopes. Method scope and instance scope. An overview is given for each.
First, the syntax is shown below.
SetInt.java
public class SetInt{
public <E> Set<E> union(Set<E> s1,Set<E> s2){
// (Example)Processing to add s1 and s2
}
}
The first thing you will notice is the
Set<Integer> result = SetInt.union(Set<Integer>,Set<Integer>);
Set<Integer> result = SetInt.<Integer>union(Set<Integer>,Set<Integer>);
In addition, exception handling can be called as follows by explicitly specifying the type.
Hoge.java
public class Hoge{
public <E extends Exception> void example() throws E {};
public void test() {
try {
this.<IOException>example();
} catch (IOException e) {
//Exception handling at IOException exception catch
}
try {
this.<SQLException>example();
} catch (SQLException e) {
//Exception handling at SQLException exception catch
}
}
}
It's more often seen than method scope, and I feel that it's usually dealt with in introductory books. An example is shown below.
Stack.java
public class Stack<E> {
private E elements;
public void push(E e) {
//Process to push to elements
}
public E pop() {
//Processing to extract E from elements
}
}
You can define associations where the arguments, return values, and instance field types of multiple declared instance methods (two above) are the same.
Knowledge of "covariance, contravariance, immutability" and "Liskov substitution principle" is a prerequisite. This article is easy to understand about the former (https://www.thekingsmuseum.info/entry/2016/02/07/235454) In the following memo, as an example, there are classes A, B, and C, respectively, and they are verified as having an inheritance relationship of C extends B and B extends A, respectively.
It can be defined as follows.
List<? extends B> extendsBList = new ArrayList<C>();
The above extendsAList has the following properties.
Aside from property 1, property 2 remains questionable. The process by which property 2 is derived is summarized below.
For example, it can be stored in extendsBList and extendsBList.add (new B ()); holds. However, please refer to the initialization code of extendsBList once again. It is initialized with ArrayList
It can be defined as follows.
List<? super B> superBList = new ArrayList<A>();
The above superBList has the following properties.
Question remains in property 2. However, the fact that property 1 holds means that the object retrieved by get () may be an object type higher than the B type. From this, when <? Super B> is set, the object obtained by get () can be received only by the Object type located at the top of all types. Instead of accepting the storage, it becomes impossible to specify the type to return.
Effective Java 2nd Edition Item 28 describes the PECS principles. Quoted below.
In the function, if the role of the generics type argument is "Producer", use extends, and if it is "Consumer", use super. A producer is an argument that creates (provides) some value in a function. On the other hand, a consumer is an argument that consumes (uses) some value in a function. This principle is also known as "PECS" for Producer-Extends and Consumer-Super.
Based on the discussion of covariance and contravariance, we can understand why we do so. Take the Hoge class as an example.
In a method, using extends as the argument of the method and super as the return value of the method can take a wider implementation of the method.
I would like to review the content of the article from time to time. Also, when I can write generics, I want to be able to design using generics.
(https://www.amazon.co.jp/exec/obidos/ASIN/B078H61SCH/xray2000-22/)
Recommended Posts