Generics of Java advanced features

What is generics

As a literal meaning, a generalized type means that a specific type cannot be determined during encoding. A placeholder (uppercase, English is recommended) needs to be used first. When running, a specific type is passed in to replace this generic tag

Why do I need generics

Pseudo demand

Suppose we need a list to store String data, the design of this structure is

class MyListForString{
  String get();
  void set(Sring int)
}

Then, if you find that you need a list to access Integer type data, you need to redefine a structure

class MyListForInteger{
  Integer get();
  void set(Integer int)
}

Then, we need a list to store xx type data..

Optimize with Object

To extract this requirement, we actually need to have a list structure, and then be able to store a specified type (this type may change according to needs), and we need to be able to correctly extract the corresponding type. Now, in order to cope with the changing requirements, we repeat writing almost MyListForXX

According to the knowledge points of polymorphism and upward transformation (for those who don't understand xdm, you can take a look at the pre knowledge points I prepared Three basic features of Java polymorphism ), we can use Object optimization to form the following code block

class MyList{
  Object get();
  void set(Object obj)
}

Now we can achieve universality, that is, we should be careful when using emm

MyList list = new MyList();
list.set(1);
list.set("1");
Integer a = (Integer)list.get();  // The type extracted from the code is actually Object. We need to manually force it to the type when saving
String s = (String)list.get();

As the pseudo code demonstrates, there are no restrictions when we store data. However, if we take it out, we need to be careful. After all, no one knows what type of data is stored in the list. Maybe we can modify the description of instance creation to achieve a little non binding restrictions?

MyList listOnlyForInt = new MyList();  // Note the instance name
list.set(1);

Generic Optimization

Perhaps fed up with this weak constraint, java finally introduced generics in JDK5

The advantage of modifying the above code with the idea of generics is obvious. When we create an instance of MyList, we tell the JVM that the instance type of generic T operated by this instance is String. The JVM will verify whether the data types match when we store the data, and automatically force it to the corresponding type when we take it out

There is no magic inside the JVM. The underlying type is still Object. However, the JVM will verify the input parameters and force the output parameters according to the specific generic types we pass

class MyList<T>{
  T get();
  void set(T int)
}
MyList<String> strs = new MyList<String>();
String s = strs.get();

java.util.ArrayList

Let's read the source code of get(), add() of the collection class ArrayList that we often use

1. The array of Object [] is actually used in the collection

2. add() stores the data into the Object array, which involves an upward transformation of the hidden operation (disguised verification, all class es are inherited from the Object)

3. get() forcibly converts the obtained data to the actual type of generic E

public E get(int index) {	// line: 432
  rangeCheck(index);
  return elementData(index);
}
E elementData(int index) {  // line: 421
  return (E) elementData[index];
}
public boolean add(E e) {  // line: 461
  ensureCapacityInternal(size + 1);  // Increments modCount!!
  elementData[size++] = e;
  return true;
}
transient Object[] elementData; // non-private to simplify nested class access  // line: 135

So this is generics

From the above example, careful students should find, "eh, I used generic duck to write list, set and map?"

Write a few casually:

List<String> list = new ArrayList<>();
Set<Integer> set = new HashSet<>();
HashMap<String, User> hashMap = new HashMap<>();

Yes, that's the basic use of generics

Boundary of generics

In the generic world, there are actually similar inheritance relationships, such as < T extensions string >, < T super string >

extends

class MyList<T extends String>{
  T get();
}

This code doesn'T seem to have changed much. The only difference is that generic T is a type inherited from String

In addition, because of the inheritance relationship, we can obtain any type of generic instance T, transform them upward and treat them as a String, and then actually call the corresponding methods in generic T through the idea of polymorphism

super

class MyList<T super String>{
  void set(T int)
}

It can be understood that the generic T passed in at runtime is the parent class of String, and the specific parent class is unknown

Differences between extensions and super

The difference between super and extensions is that generic operations using < T extensions XXX > can be treated as XXX. Generics using < T super XXX > can only pass the instance corresponding to XXX to generic type processing

Type Erasure

Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
Class c3 = new ArrayList().getClass();
System.out.println(c1 == c2);	// true
System.out.println(c1 == c3);	// true

From the above code, although ArrayList declares specific generics or does not have generics, their corresponding classes are all the same? In disguise, there is no trace of generics in class?

Generics on Methods

Although the whole article talks about generics in class, in fact, generics can also be applied and used in methods

However, generics cannot be initialized directly. We need to pass in a specific generic instance or the corresponding generic class

The definition format of generic method is [scope] [static] < T > t method name (parameter list)

Take a jsonutil I encapsulated Toobject(), for example, is defined as public static < T > t toobject()

public static <T> T toObject(String json, Class<T> clz) {
  if (json == null) {
    try {
      return clz.newInstance();
    } catch (Exception e) {
      LogUtil.exception("JsonUtil.toObject: <T>", e);
    }
  }
  return json_.toObject(json, clz);
}
public static <T> T toObject(String json, T t) {	// Alternatively, we can pass in a specific instance of the generic, but this is very rare
  toObject(json, t.getClass());
}
// Pseudo code call
User user = JsonUtil.toObject(jsonStr, User.class);

Generic Tags

In this article, the tag T is widely used to represent generics. We can also replace it with other symbols, but it is required to declare that this is a generic operation in the way of < tag >, such as demonstration code

class List<E>{}

<A> A get();

<A, B, C> Tuple<A,B,C> tupleInstance(){}

tuple

Forget to introduce a fun structure with generics, tuple. The meaning of this data structure is to return multiple parameters for the method. Of course, we can also use map and list, but it is not as elegant as tuple

First, we define a tuple structure that returns two generic types

class Tuple<A, B>{
  final A a;
  final B b;
  public Tuple(A a1, B b1){
    a = a1;
    b = b1;
  }
}

Just like initializing an ordinary class, there is no difference in usage. The change is the two fields inside the tuple. Their classes are determined at runtime. We can change their types at will when necessary

Tuple<Integer, String> tuple = new Tuple<>(1, "1");
Integer a = tuple.a;
String b = tuple.b;

# ❤ last
It's not easy to create. If this blog is helpful to you, I hope you can click three times!, Thanks for your support. I'll see you next time~~~

Keywords: Java Programming Redis data structure

Added by -Karl- on Mon, 20 Dec 2021 10:42:45 +0200