This is the 28th day of my participation in the August Text Challenge.More challenges in August

An overview of the

Generics, or 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 (to control the types of parameters that are specifically restricted by the different types specified by generics without creating new types). That is, in the use of generics, the data type of the operation is specified as a parameter, which can be used in classes, interfaces, and methods, called generic classes, generic interfaces, and generic methods, respectively.

The sample

public static void main(String[] args) {
    List arrayList = new ArrayList();
    arrayList.add("hello");
    arrayList.add(100);
​
    for(int i = 0; i< arrayList.size(); i++){ String item = (String)arrayList.get(i); System.out.println("item = "+ item); }}Copy the code

Running results:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Copy the code

ArrayList can hold any type. In this example, we added a String and an Integer, and then used them as strings, so the program crashed.

Generics were created to solve problems like this, which can be solved at compile time.

If we change the first line of code that declares initializing the list, the compiler will help us find problems like this at compile time.

List<String> arrayList = new ArrayList<String>(); . //arrayList.add(100); At compile time, the compiler will report an errorCopy the code

features

Generics are only valid at compile time. Look at the following code:

public static void main(String[] args) {
    List<String> stringList = new ArrayList<String>();
    List<Integer> integerList = new ArrayList<Integer>();
    Class stringListClass = stringList.getClass();
    Class integerListClass = integerList.getClass();
    if(stringListClass.equals(integerListClass)){
        System.out.println("Generic test, same type"); }}Copy the code

Output: Generic test, same type.

As the above example demonstrates, generics in Java are only valid at compile time. When the result of generics is properly verified during compilation, information about generics is erased, and methods for type checking and casting are added at the boundary between the object entering and leaving the method. That is, generic information does not enter the runtime phase.

To sum it up: Generic types are logically seen as multiple different types, but are actually the same basic type.

The use of generics

Generics can be used in three ways: generic classes, generic interfaces, and generic methods

A generic class

Generic types are used in the definition of classes and are called generic classes. Generics make it possible to open the same interface to operations on a set of classes. The most typical are the various container classes: List, Set, Map.

The most basic way to write generic classes is:

classClass name < generic identifier: You can write any identifier to identify the type of the specified generic >{
  privateGeneric id/* (member variable type) */ var; . }}Copy the code

One of the most common generic classes:

Here, T can be written as any identifier. Common parameters such as T, E, K, and V are often used to represent generics. When instantiating generic classes, the specific type of T must be specified.

public class Jw<T> {

    private T key;// Key is a member variable of type T, whose type is externally specified

    public Jw(T key){// The generic constructor parameter key is also of type T, whose type is externally specified
        this.key = key;
    }

    public T getKey(a){// The generic method getKey returns a value of type T, the type of which is externally specified
        returnkey; }}Copy the code
public static void main(String[] args) {
    // 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
    Jw<Integer> integerJw = new Jw<Integer>(111);
    // The argument type passed in must be of the same type as the generic type parameter, that is, String
    Jw<String> stringJw = new Jw<String>("Hello");
    System.out.println("integerJw = " + integerJw.getKey());
    System.out.println("stringJw = "+ stringJw.getKey()); } integerJw =111
stringJw = Hello
Copy the code

Is it necessary to pass in a generic type argument to a defined 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.

public static void main(String[] args) {
    Jw jw1 = new Jw(1111);
    Jw jw2 = new Jw("HELLO");
    Jw jw3 = new Jw(44.56);
    Jw jw4 = new Jw(true);

    System.out.println("jw1 = "+jw1.getKey());
    System.out.println("jw2 = "+jw2.getKey());
    System.out.println("jw3 = "+jw3.getKey());
    System.out.println("jw4 = "+jw4.getKey()); } output: jw1 =1111
jw2 = HELLO
jw3 = 44.56
jw4 = true
Copy the code

Note:

  • The type parameters of a generic type can only be class types, not simple types.
  • Cannot be used for exact generic typesinstanceofOperation. The following operations are illegal and will cause errors during compilation.
if(example instanceof Jw<String>){} 
Copy the code

A generic interface

Generic interfaces are defined and used in much the same way as generic classes.

// Define a generic interface
public interface Parent<T> {
    public T next(a);
}
Copy the code

When a class that implements a generic interface passes no generic arguments:

class son<T> implements Parent<T>{
    @Override
    public T next(a) {
        return null; }}Copy the code

When a class that implements a generic interface passes in a generic argument:

class son implements Parent<String>{

    private String[] fruits = new String[]{"Apple"."Banana"."Pear"};

    @Override
    public String next(a) {
        Random rand = new Random();
        return fruits[rand.nextInt(3)]; }}Copy the code

Generic method

Generic classes specify the specific type of the generic when instantiating the class.

A generic method specifies the specific type of the generic when calling the method.

/** * A basic introduction to generic methods *@paramThe generic argument * passed in by tClass@return<T> <T> <T> <T> <T> * 2) Only methods that declare <T> are generic, and member methods that use generics in a generic class are not generic. * 3) <T> indicates that the method will use the generic type T before you can use the generic type T in the method. * 4) Like the definition of generic classes, here T can be written as any identifier, common parameters such as T, E, K, V are often used to represent generics. * /
public <T> T parentMethod(Class<T> tClass)throws InstantiationException ,
  IllegalAccessException{
        T instance = tClass.newInstance();
        return instance;
}

public static void main(String[] args) {
    try {
        Object obj = Test03.parentMethod(Class.forName("fanxing.Test04"));
        System.out.println(obj);
    } catch(InstantiationException | IllegalAccessException | ClassNotFoundException e) { e.printStackTrace(); }}Copy the code

Class

Generic methods can be used anywhere and in any scenario. There is, however, a very special case when a generic method appears in a generic class, as follows:

package fanxing;

public class Test05 {

    static class Animal{
        @Override
        public String toString(a) {
            return "Animal"; }}class cat extends Animal{
        @Override
        public String toString(a) {
            return "cat"; }}static class Person{
        @Override
        public String toString(a) {
            return "Person"; }}static class Test05Test<T>{
        public void show_1(T t){
            System.out.println(t.toString());
        }

        // We declare a generic method in the generic class, using the generic T. Note that T is a completely new type and may not be the same type as T declared in the generic class.
        public <T> void show_2(T t){
            System.out.println(t.toString());
        }

        // Declare a generic method in a generic class, using the generic type E, which can be of any type. It could be of the same type as T, or it could be different.
        // Since generic methods declare generics 
      
        when declared, the compiler can correctly recognize generics recognized in generic methods even if they are not declared in generic classes.
      
        public <E> void show_3(E t){ System.out.println(t.toString()); }}public static void main(String[] args) {
        Animal animal = new Animal();
        Person person = new Person();

        Test05Test<Animal> test05Test = new Test05Test<Animal>();
        // Apple is a subclass of Fruit, so this is ok
        test05Test.show_1(animal);
        // The compiler will report an error because the generic type argument is Fruit and the passed argument class is Person
        //test05Test.show_1(person);

        // Use both methods successfully
        test05Test.show_2(animal);
        test05Test.show_2(person);

        // Both methods can also be successfultest05Test.show_3(animal); test05Test.show_3(person); }}Copy the code

Generic methods and mutable parameters

Another example of generic methods and mutable parameters:

public class Test {

    public static <T> void printMsg(T... args){
        for(T t : args){
            System.out.println("t = "+ t); }}public static void main(String[] args) {
        printMsg("cat".222."hello".55.68); }} t = cat t =222
t = hello
t = 55.68
Copy the code

Static methods versus generics

If static methods are to use generics, they must also be defined as generic.

public class Test<T> {... ./** * If you define a static method that uses generics in a class, you need to add an additional generic declaration (to make the method generic) * even if the static method uses generics already declared in the generic class. Public static void show(T T){.. } Test cannot be refrenced from static context */
    public static <T> void show(T t){}}Copy the code

Summary:

Generic methods allow methods to change independently of classes. Here is a basic guideline:

Whenever you can, you should use the generic approach. That is, if you use a generic method to generalize an entire class, then you should use a generic method.

In addition, for a static method, there is no access to the parameters of the generic type. So if a static method is to use generic capabilities, it must be generic.

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(Parent<? extends Number> obj){
    System.out.println("key value is " + obj.getKey());
}

public void test(a) {
    Parent<String> parent1 = new Parent<String>("hello");
    Parent<Integer> parent2 = new Parent<Integer>(50);
    Parent<Float> parent3 = new Parent<Float>(2.4 f);
    Parent<Double> parent4 = new Parent<Double>(3.14);
    // This line of code prompts an error because String is not a subclass of Number
    // show(parent1);
    show(parent2);
    show(parent3);
    show(parent4);
}
Copy the code

If we change the definition of generic classes as well:

public class Parent<T extends Number>{
    private T key;

    public Parent(T key) {
        this.key = key;
    }

    public T getKey(a){
        returnkey; }}Copy the code

This line of code also returns an error because String is not a subclass of Number

Parent<String> parent1 = new Parent<String>("hello");
Copy the code

As you can see from the two examples above, the upper and lower bounds of a generic must be added with the declaration of the generic.

conclusion

Referring to generics, I believe that we use the most is in the collection, in fact, in the actual programming process, they can use generics to simplify the development, and can very good code quality assurance. Follow me, a new era of migrant workers who focus on sharing Java knowledge.