Autumn move! Friends, you don t even know about generics now! No way, no way!

Autumn move! Friends, you don t even know about generics now! No way, no way!

1. the basic concept of generics

Definition of generic type: Generic type is a new feature of JDK 1.5, its essence is the application of parameterized type (Parameterized Type), that is to say, the data type that is operated is designated as a parameter, when it is used in Specify the specific type. This parameter type can be used in the creation of classes, interfaces, and methods. They are called generic classes, generic interfaces, and generic methods, respectively.

The idea of generics began to take root as early as in the templates of the C++ language. When the Java language is in a version where generics have not yet appeared, it can only be achieved through the combination of the two characteristics of Object being the parent of all types and type coercion. To achieve type generalization.

For example, in the access of the hash table, the get() method of HashMap was used before JDK 1.5, and the return value is an Object object. Since all types in the Java language inherit from java.lang.Object, the Object is transformed into any object. Chengdu is possible.

But because there are unlimited possibilities, only the programmer and the runtime virtual machine know what type of object this Object is. During compilation, the compiler cannot check whether the forced transformation of this Object is successful. If you only rely on the programmer to ensure the correctness of this operation, many of the risks of ClassCastException will be passed on to the program runtime.

The use of generic technology in C# and Java seems to be the same, but there are fundamental differences in implementation. Generics in C# are both in the source code of the program and in the compiled IL (Intermediate Language, at this time). Generics are a placeholder) or CLR at runtime. List<int> and List<String> are two different types, which are generated during system runtime and have their own virtual method table. And type data, this realization is called type inflation, and the generics implemented based on this method are called true generics.

Generics in Java language are different. It only exists in the source code of the program. In the compiled bytecode file, it has been replaced with the original raw type (Raw Type, also known as the bare type), and The cast code is inserted in the corresponding place, so for the runtime Java language, ArrayList<int> and ArrayList<String> are the same class. Therefore, the generic technology is actually a syntactic sugar of the Java language. The implementation method of generics in the Java language is called type erasure, and the generics implemented based on this method are called pseudo-generics. (Type erasure will be learned later)

The program code written using the generic mechanism is safer and more readable than the code that uses Object variables messily and then performs type conversion. Generics are especially useful for collections.

Generic Programming means that the written code can be reused by many different types of objects.

Case Analysis:

Before JDK1.5, Java generic programming was implemented by inheritance. Because the Object class is the base class of the class used, you only need to maintain a reference to the Object type. For example, ArrayList only maintains an array referenced by Object: public class ArrayList//Before JDK1.5

{ public Object get ( int i) {......} public void add (Object o) {......} ...... private Object[] elementData; } Copy code

There will be two problems:

No error checking, you can add objects of the class to the array list

When fetching elements, you need to perform a coercive type conversion

In this way, errors can easily occur, such as:

/**The writing before jdk1.5 is prone to problems*/ ArrayList arrayList1= new ArrayList(); arrayList1.add( 1 ); arrayList1.add( 1L ); arrayList1.add ( "ASA" ); int I = (Integer) arrayList1.get ( . 1 ); //not knowing the type of the value taken out, when the type of the conversion error-prone replication Code

The first element here is a long integer, and you thought it was an integer, so an error occurred during the forced conversion.

and so. After JDK1.5, generics were added to solve similar problems. For example, use generics in ArrayList:

/** Add generics after jdk1.5*/ ArrayList<String> arrayList2= new ArrayList<String>(); //Limit the type in the array list //arrayList2.add(1);//Because the type is limited, So you can t add integers //arrayList2.add(1L);//Because the type is limited, you can t add integers arrayList2.add( "asa" ); //You can only add strings String str=arrayList2.get( 0 ); //knowing the type of the value taken out, there is no need for cast duplicated code

It should also be understood that generic features are forward compatible. Although many classes in the standard library of JDK 5.0, such as the collection framework, have been generic, the existing code that uses collection classes (such as HashMap and ArrayList) can continue to work in JDK 1.5 without modification. Of course, existing code that does not take advantage of generics will not win the type safety benefits of generics.

Before learning generics, let s briefly introduce some basic terms of generics, and briefly introduce ArrayList<E> and ArrayList<Integer>:

  • The whole becomes an ArrayList generic type

  • The E in ArrayList<E> is called type variable or type parameter

  • The entire ArrayList<Integer> is called a parameterized type

  • The integer in ArrayList<Integer> is called the instance of the type parameter or the actual type parameter

  • The <Integer> in ArrayList <Integer> is pronounced as typeof Integer

  • ArrayList is called primitive type

2. the use of generics

Generic parameter types can be used in the creation of classes, interfaces, and methods, which are called generic classes, generic interfaces, and generic methods, respectively. Let's take a look at how it is defined.

2. 1. The definition and use of generic classes

A generic class is a class with one or more type variables. Defining a generic class is very simple, just add <> after the class name, and then add the type parameters:

class Pair < T> { private T value; public Pair (T value) { this .value=value; } public T getValue () { return value; } public void setValue (T value) { this .value = value; } } Copy code

Now we can use this generic class:

public static void main (String[] args) throws ClassNotFoundException { Pair<String> pair = new Pair<String>( "Hello" ); String str = pair .getValue(); System.out.println(str); pair .setValue( "World" ); str = pair .getValue(); System.out.println(str); } Copy code

The Pair class introduces a type variable T, enclosed in angle brackets <>, and placed after the class name. A generic class can have multiple type variables. For example, you can define the Pair class, where the first domain and the second domain use different types:

public class Pair < T,U>{...} Copy code


It is common for type variables to be capitalized and relatively short. In the Java library, the variable E is used to represent the element type of the collection, and K and V represent the types of keywords and values, respectively. (You can also use the adjacent letters U and S when necessary) to indicate "any type".

2.2. Definition and use of generic interfaces

Defining a generic interface is similar to a generic class. Look at the following simple example:

interface Show<T,U>{ void show (T t,U u) ; } class ShowTest implements Show < String,Date>{ @ Override public void show (String str,Date date) { System.out.println(str); System.out.println(date); } } Copy code

have a test:

public static void main (String[] args) throws ClassNotFoundException { ShowTest showTest = new ShowTest(); "Hello" , new Date()); } Copy code

3.3, the definition and use of generic methods

Generic classes enforce type constraints among multiple method signatures. In List< V >, the type parameter V appears in the signatures of methods such as get(), add(), and contains(). When creating a variable of type Map<K, V>, you declare a type constraint between the methods. The value you pass to add() will be of the same type as the value returned by get().

Similarly, the reason for declaring a generic method is generally because you want to declare a type constraint between multiple parameters of the method.

Give a simple example:

public static void main (String[] args) throws ClassNotFoundException { String str=get( "Hello" , "World" ); System.out.println(str); } public static <T, U> T get (T t, U u) { if (u != null) return t; else return null; } Copy code

3. the type limit of generic variables

In the above, we simply learned generic classes, generic interfaces and generic methods. We all directly use the form of <T> to complete the declaration of generic types.

Sometimes, classes, interfaces, or methods need to constrain type variables. Look at the following example:

There is such a simple generic method:

public static <T> T get (T t1,T t2) { if (t1.compareTo(t2)>= 0 ); //Compile error return t1; } Copy code

Because, before compilation, that is, when we are still defining this generic method, we don't know what type of generic type T is, so we can only default T to the primitive type Object. So it can only call those methods from Object, not the compareTo method.

But my intention is to compare t1 and t2, what should I do? At this time, it is necessary to use type qualification and set a bound on the type variable T to achieve this.

We know that all methods that implement the Comparable interface will have a compareTo method. Therefore, the following restrictions can be made on <T >:

public static <T extends Comparable> T get (T t1,T t2) { //Add type qualification if (t1.compareTo(t2)>= 0 ); return t1; } Copy code

Type restrictions can be used in generic classes, generic interfaces and generic methods, but pay attention to the following points:

  1. Regardless of whether the restriction is a class or an interface, the keyword extends is used uniformly

  2. You can use the ampersand to give multiple restrictions, such as

public static <T the extends the Serializable the Comparable &> T GET (T T1, T T2) copying the code
  1. If there are both interfaces and classes, then there must be only one class and put it in the first position
public static <Object & T the extends the Serializable the Comparable &> T GET (T T1, T T2) copying the code

4. the reflection mechanism

4.1 Basic description

The reflection mechanism can obtain the complete structure information of the class while the program is running, and can dynamically manipulate attributes and methods. To understand the reflection mechanism, you must have a clear understanding of class compilation, JVM loading, and runtime data area. This content can be moved to the JVM series of articles.

Obtain the structure of the class dynamically at runtime, and then dynamically create objects and manipulate properties and methods. This method is not used in actual development. This obviously consumes JVM resources and ignores some encapsulation and leads to security problems.

2. Reflective class library

  • java.lang.Class: Class Class

  • java.lang.reflect.Constructor: Constructor

  • java.lang.reflect.Field: attribute

  • java.lang.reflect.Method: Method

4.2 Class object of API

The common way to obtain the Class object of the target type is to obtain the relevant structure information through the Class object to operate or access:

public static void main (String[] args) throws Exception { //Class object goes back User user1 = new User( 1 , "name01" ); Class userClass1 = user1.getClass(); Class userClass2 = Class.forName( "" ); Class userClass3 = User.class; System.out.println(User.class.getName()); System.out.println( "userClass1==userClass2?" +(userClass1==userClass2)); System.out.println( "userClass2==userClass3?" +(userClass2==userClass3)); //Type creation and judgment Object object = User.class.newInstance(); System.out.println( "Type:" +(object instanceof User)); System.out.println( "Type:" +(userClass3.isInstance(user1))); } Copy code

Output result: Here is a note: through the newInstance() method of the Class object, that is, the parameterless constructor based on the User class, the User class is first required to have a parameterless construction method.

4.3 Constructor of API

Class object reads the construction method, you can obtain all the construction methods, the construction methods of different modified types, or according to the specified type of the construction parameter:

public static void main (String[] args) throws Exception { Class userClass = User.class; //Read the public construction method Constructor[] userConArr = userClass.getConstructors(); printCon(userConArr); //Read the specified private construction method Constructor privateCon = userClass.getDeclaredConstructor(Integer.class); System.out.println(privateCon); //Read all construction methods userConArr = userClass.getDeclaredConstructors(); printCon(userConArr); //Call the public construction method to create an object Constructor pubCon = userClass.getConstructor(Integer.class,String.class); Object pubUser = pubCon.newInstance( 1 , "hello" ); //Call the private constructor to create the object Constructor priCon = userClass.getDeclaredConstructor(Integer.class); //Ignore the private permission modifier priCon.setAccessible(Boolean.TRUE); Object priUser = priCon.newInstance( 2 ); System.out.println(pubUser+ "\n" +priUser); } public static void printCon (Constructor[] constructors) { for (Constructor constructor:constructors){ System.out.println(constructor); } } Copy code

It should be noted here that by calling the setAccessible(Boolean.TRUE) method, you can create an object based on a private construction method. This obviously violates the basic design principles of Java and destroys the security of the code.

4.4 Field attribute of API

Field guarantees the properties, modifiers, value management and other related operations of member variables:

public static void main (String[] args) throws Exception { Class userClass = User.class; //Get public fields Field[] pubArr = userClass.getFields(); printField(pubArr); //Get all fields Field[] fieldArr = userClass.getDeclaredFields(); printField(fieldArr); //Get the specified field Field emailField = userClass.getField( "email" ); Field nameField = userClass.getDeclaredField( "name" ); printField( new Field[]{emailField,nameField}); //Create objects and manipulate properties Object userObj = userClass.newInstance(); nameField.setAccessible(Boolean.TRUE); nameField. set (userObj, "world" ); emailField. set (userObj, "" ); System.out.println( "userObj:" +userObj); } /** * Print member variable information */ public static void printField (Field[] fields) { for (Field field: fields){ System.out.println( "Declaration:" +field); UserAnno userAnno = field.getAnnotation(UserAnno.class); System.out.println( "Comment:" +userAnno.desc()); String fieldName = field.getName(); System.out.println( "Name:" +fieldName); Type type = field.getGenericType(); System.out.println( "Type:" +type); } } Copy code

Note that the type information obtained here is very useful in some specific business scenarios.

4.5 Method Method of API

public static void main (String[] args) throws Exception { Class userClass = User.class; //Get all public methods [including parent class and Object class methods] Method[] pubMethods = userClass.getMethods(); printMethod(pubMethods); //Get all methods Method[] allMethods = userClass.getDeclaredMethods(); printMethod(allMethods); //Get the specified method Method method = userClass.getMethod( "parName" ,String.class); printMethod( new Method[]{method}); //call method Object userObj = userClass.newInstance(); Method setId = userClass.getDeclaredMethod( "setId" , Integer.class); setId.invoke(userObj, 99 ); Method setName = userClass.getDeclaredMethod( "setName" , String.class); setName.invoke(userObj, "java" ); Method sayHi = userClass.getDeclaredMethod( "sayHi" , String.class); sayHi.setAccessible(Boolean.TRUE); sayHi.invoke(userObj, "c++" ); System.out.println(userObj); } /** * Printing method information */ public static void printMethod (Method[] methods) { for (Method method: methods){ System.out.println( "Definition:" +method); System.out.println( "Name:" +method.getName()); UserAnno userAnno = method.getAnnotation(UserAnno.class); if (userAnno != null){ System.out.println( "Comment:" +userAnno.desc()); } Type[] paramTypeArr = method.getParameterTypes(); for ( int i = 0 ; i< paramTypeArr.length; i++){ System.out.print( "Parameter" +(i+ 1 )+ "Type:" +paramTypeArr[i]+ ";" ); } System.out.println( " Number of parameters:" +method.getParameterCount()); } } Copy code

Note that the acquisition of methods here is far more than the definition of the class itself, including inherited from the parent class, and the Java basic Object class.