Which leads to the generic

We use the following example to introduce the concept of generics.

public class Test {

    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("abc");
        list.add(2);

        for (int i = 0; i < list.size(); i++) {
            String name = (String) list.get(i); // error
            System.out.println("name:"+ name); }}}Copy the code

When access to the second element in the list of complains, Java. Lang. ClassCastException: Java. Lang. Integer always be cast to Java. Lang. String. This is a common conversion error.

When we put an element into the list, we do not use the specified type. We use the default Object type when we retrieve the element. Therefore, type casting exceptions are easy to occur.

We want to accomplish as a result, the element set to remember each type, and can reach as long as compile time don’t appear problem, won’t appear the runtime Java. Lang. ClassCastException. Generics are just what we need.

What are generics?

Generics, parameterized types. When it comes to parameters, you’re most familiar with defining tangible arguments to a method and then passing the arguments when the method is called. So what about parameterized types? As the name implies, a type is parameterized from an original concrete type, similar to a variable parameter in a method. In this case, the type is also defined as a parameter (called a type parameter), and then passed in the concrete type (type argument) when the/is used.

The nature of generics is to parameterize types, that is, to control the types of parameters that are specifically restricted by the different types specified by the generics without creating new types. In the use of generics, the data type of an operation is specified as a parameter that can be used in classes, interfaces, and methods, called generic classes, generic interfaces, and generic methods, respectively.

Characteristics of generics

The introduction of generics into the Java language is a major enhancement. Not only have the language, type system, and compiler changed significantly to support generics, but the class library has been overhauled so that many important classes, such as the collections framework, have become generic. This brings many benefits:

  1. Type safety. The primary goal of generics is to improve type safety in Java programs. By knowing the type limits of variables defined using generics, the compiler can verify type assumptions to a much higher degree.
  2. Eliminate casts. A side benefit of generics is the elimination of many casts from the source code. This makes the code more readable and reduces the chance of errors.
  3. Potential performance gains. Generics make greater optimization possible. In the initial implementation of generics, the compiler inserts casts (which the programmer would have specified in the absence of generics) into the generated bytecode.

Named type parameter

The recommended naming convention is to use uppercase single-letter names as type arguments. For common generic patterns, the recommended name is:

  • K: keys, such as mapped keys
  • V: values, such as the contents of a List and Set, or values in a Map
  • E: the element
  • T: generics
public class Generic<T> { 
    // Key is of type T
    private T key;

    public Generic(T key) { 
    	// The generic constructor parameter key is also of type T
        this.key = key;
    }

    public T getKey(a) { 
    	// The generic method getKey returns a value of type T
       returnkey; }}Copy the code

As defined above, a normal generic class has a member variable of type T, and the type of T is externally specified. The same is true for generic methods and generic constructors.


Generic<Integer> genericInteger = new Generic<Integer>(123456); / / 1

Generic<String> genericString = new Generic<String>("key_vlaue"); / / 2

System.out.println("key is " + genericInteger.getKey());
System.out.println("key is " + genericString.getKey());
Copy the code

The type parameters of a generic type can only be class types (including custom classes), not simple types. The type of the argument passed in must be the same as the type parameter of the generic type, that is, Integer/String.

As mentioned above, is it necessary to pass in a generic type argument to define a generic class?

This is not the case. When you use generics, if you pass in a generic argument, the generic argument will be restricted accordingly, and the generics will do what they are supposed to do. A method or member variable defined using a generic type in a generic class can be of any type, provided that no generic type argument is passed.

Generic genericString = new Generic("111111");
Generic genericInteger = new Generic(4444);

System.out.println("key is " + genericString.getKey());
System.out.println("key is " + genericInteger.getKey());
Copy the code

The code snippet above will output the following:

key is 111111
key is 4444
Copy the code

A generic guard or member variable used in a generic class can be of any other type, such as Integer or String, without passing in a generic type argument. Note, however, that the type parameters of a generic type can only be class types, not simple types. And you cannot use the Instanceof operation on an exact generic type. Is the type of the corresponding object instance generated the same for each type argument passed in? See the following example:

public class GenericTest {

    public static void main(String[] args) {

        Generic<Integer> name = new Box<String>("111111");
        Generic<String> age = new Box<Integer>(712);

        System.out.println("name class:" + name.getClass());  
        System.out.println("age class:" + age.getClass()); 
        System.out.println(name.getClass() == age.getClass());    // true}}Copy the code

The output structure shows that when using a Generic class, although was introduced into the different Generic argument, but not in the true sense to generate different types, the incoming different Generic argument of a Generic class has only one in the memory, which is the most basic of same type (in this case, the Generic), logically, of course, we can understand into a number of different Generic types.

The reason for this is that the concept of generics in Java is intended only at the compile stage. During compilation, information about generics is erased when the result of generics is properly verified. That is, the successfully compiled class file does not contain any generic information. Generic information does not enter the runtime phase.

Generic types are logically seen as multiple different types, but are actually the same basic type.

The wildcard

Ingeter is a subclass of Number, and Generic

is actually the same basic type as Generic

. The question is, can you pass in an instance of Generic

in a method that uses Generic

as a parameter? Can logical types like Generic

and Generic

be considered parent-child Generic types? Let’s verify this by defining a method.





public void show(Generic<Number> obj) {
    System.out.println("key value is " + obj.getKey());
}
Copy the code

Make the following call:

Generic<Integer> genericInteger = new Generic<Integer>(123);

show(genericInteger);  //error Generic
      
        cannot be applied to Generic
       
      
Copy the code

We can see from the prompt that Generic

cannot be considered a subclass of Generic

. As you can see, there can be multiple versions of the same generic (because parameter types are indeterminable), and different versions of generic class instances are not compatible.

We cannot therefore define a show(Generic

obj) to handle, so we need a reference type that logically represents both Generic and Generic parent classes. This is where the type wildcard comes in.

Generic letters such as T, K, V, and E are typed, and the type parameters are assigned specific values. In addition to having types, you can also use wildcards to express types,? An unknown type, whose type parameters are given uncertain values, can only be used to declare types and method parameters, not to define generic classes. Rewrite the method as follows:

public void show(Generic<? > obj) { System.out.println("key value is " + obj.getKey());
}
Copy the code

Here? Is a type argument, not a type parameter. Number, String, and Integer are all real types. As the parent of all types, it is a real type. So if you don’t know what the type is, what’s the wildcard? ; When operating on types without the specific functions of the type, only the functions of the Object class are used. So you can use it? Wildcard to list unknown types.

Generic upper and lower bounds

When using generics, we can also restrict the upper and lower bounds of generic type arguments passed in. For example, type arguments can only be passed in the parent or subclass of a type. Adds an upper boundary to a generic, meaning that the type argument passed in must be a subtype of the specified type.

public void show(Generic<? extends Number> obj) {
    System.out.println("key value is " + obj.getKey());
}
Copy the code

Our input parameter in the generic method qualifies subclasses of parameter type Number.

Generic<String> genericString = new Generic<String>("11111");
Generic<Integer> genericInteger = new Generic<Integer>(2222);


showKeyValue1(genericString); // error
showKeyValue1(genericInteger);

Copy the code

Compile error when our input is String because String is not a subclass of Number.

The type wildcard upper limit is passed through the Generic
extends Number; Correspondingly, the type wildcard lower bound is Generic
form, which is the opposite of the type wildcard upper limit, will not be explained here.

Generic array

There is no way to create an exact generic array in Java, i.e. :

List<String>[] ls = new ArrayList<String>[10];  
Copy the code

It is possible to create generic arrays using wildcards:

List<? >[] ls =newArrayList<? > [10]; 

//List<String>[] ls = new ArrayList[10];
Copy the code

JDK1.7 simplifies generics, so an alternative declaration is also possible.

Due to the erasing mechanism of JVM generics, generics information is not known to the JVM at run time. Generic arrays Actual run-time Object arrays can only be primitives (T[] for Object[], Pair[] for Pair[]), whereas actual run-time array objects may be of type T (although they are erased to primitives at run time). The only way to successfully create a generic array is to create a new array of erased types and then cast it.

public class GenericArray<T> {
    private Object[] array;  // Maintain an Object[] array
    @SupperessWarning("unchecked")
    public GenericArray(int v) {
        array = new Object[v];
    }
    public void put(int index, T item) {
        array[index] = item;
    }
    public T get(int index) { 
    	return (T)array[index]; 
    } // Array object exit strong
    public T[] rep() { return (T[])array; } // The runtime is of type Object[] anyway
    public static void main (String[] args){
        GenericArray<Integer> ga = new GenericArray<Integer>(10);
        // Integer[] ia = ga.rep(); / / still ClassCastException
        Object[] oa = ga.rep(); // Can only return an array of type Object[]
        ga.put(0.11);
        System.out.println(ga.get(0)); / / 11}}Copy the code

At runtime, the exit of the array object is converted to output, while the entry method is type-safe at compile time, so the exit method can safely cast to ensure success.

summary

This article focuses on the concepts and applications of Java generics. Generics enable the compiler to check types at compile time to improve type safety and reduce exceptions thrown at run time due to object type mismatches. The birth of generics introduces concepts related to how generics can be used to simplify development while maintaining code quality.

Subscribe to the latest articles, welcome to follow my official account

reference

Java generics in detail