Java learning road-generic

Java learning road-generic

Overview

What is generic? Java generics (generics) is a new feature introduced in JDK 5. Generics provide a compile-time type safety detection mechanism, which allows programmers to detect illegal types at compile time.

The essence of generics is a parameterized type, which means that the type of data being manipulated is specified as a parameter.

The essence of generics is to parameterize types (in the case of not creating a new type, through the different types specified by generics to control the type of specific restrictions on formal parameters) . That is to say, in the process of using generics, the data type of the operation is designated as a parameter. This parameter type can be used in classes, interfaces, and methods, which are called generic classes, generic interfaces, and generic methods, respectively.

One, generic method

Generic methods can receive different types of parameters when called. The compiler will handle each method call appropriately according to the type of parameters passed to the generic method.

Follow the following rules when defining generic methods:

  • Generic methods need to declare that there is a type parameter declaration part (separated by angle brackets), the type parameter declaration part is before the method return type;
  • Each type parameter declaration part contains one or more type parameters, separated by commas. A generic parameter, also known as a type variable, is an identifier used to specify the name of a generic type;
  • Type parameters can be used to declare the return value type, and can be used as a placeholder for the actual parameter type obtained by a generic method;
  • Type parameters can only represent reference data types and cannot be used for basic data types;
  • The declaration of the generic method body is the same as other methods.

Example:

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        Integer[] a1 = {
    
    1, 2, 3};
        Double[] a2 = {
    
    1.0, 2.0, 3.0};
        Character[] a3 = {
    
    'a', 'b', 'c'};

        Demo.print(a1);  // 1 2 3
        Demo.print(a2);  // 1.0 2.0 3.0 
        Demo.print(a3);  // a b c
    }

    public static <E> E[] print(E[] input) {
    
    
        for (E i : input) {
    
    
            System.out.print(i + " ");
        }
        System.out.println();
        return input;  // 返回值也为泛型
    }
}

Two, generic class

The type parameter declaration part of a generic class also contains one or more type parameters, separated by commas.

A generic parameter, also known as a type variable, is an identifier used to specify the name of a generic type. Because they accept one or more parameters, these classes are called parameterized classes or parameterized types.

import java.util.Arrays;

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        Integer[] a1 = {
    
    1, 2, 3};
        Box<Integer[]> b1 = new Box<>(a1);
        System.out.println(Arrays.toString(b1.getT()));

        Character[] a2 = {
    
    'a', 'b', 'c'};
        Box<Character[]> b2 = new Box<>(a2);
        System.out.println(Arrays.toString(b2.getT()));
    }
}

class Box<T> {
    
    
    private final T t;

    public Box(T t) {
    
    
        this.t = t;
    }

    public T getT() {
    
    
        return t;
    }
}

Three, generic interface

Implement generic interface also simply the interface name <类型名>on the line.

But there are two ways to implement the interface class:

If the subclass that implements the interface does not want to use generic declarations, just specify its specific operation type directly when implementing the interface. For example MyClass1;

If the subclass that implements the interface wants to use generic declarations, then use generics when implementing the interface. For example MyClass2.

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        MyClass1 m1 = new MyClass1("Hello");
        System.out.println(m1.getValue());;

        MyClass2<Integer> m2 = new MyClass2<>(1);
        System.out.println(m2.getValue());;
    }
}

interface info<T> {
    
    
    T getValue();
}

// 指定具体的类
class MyClass1 implements info<String> {
    
    
    private final String value;

    public MyClass1(String value) {
    
    
        this.value = value;
    }

    @Override
    public String getValue() {
    
    
        return value;
    }
}

// 泛型类
class MyClass2<T> implements info<T> {
    
    
    private final T value;

    public MyClass2(T value) {
    
    
        this.value = value;
    }

    @Override
    public T getValue() {
    
    
        return value;
    }
}

Four, wildcard

When we use Fanhua, the generics around the equal sign need to be consistent, but you may not know what the generic on the right side of the equal sign is. At this time, Java provides us with generic wildcards, which can be used on the left side of the equal sign.

Before understanding wildcards, we must first understand the relationship between Java references and instances:

In Java, a reference is equivalent to a container, and an instance is equivalent to water in the container. Under normal circumstances, water can only be filled in a container that is equal to or larger than itself (upward transition: instances of subclasses are loaded into the container of the parent class). But it can also be loaded into a container smaller than itself (downcast: the instance of the parent class is loaded into the container of the subclass), but this step is very dangerous and there will be many restrictions. The generic is to solve the problem of insecure downcasting .

Below we use the relationship between fruit plate and fruit to explain wildcards:

First create a plate class:

// 水果盘类
class Plate<T> {
    
    
    private T item;
    public Plate(T item) {
    
    
        this.item = item;
    }
    public void set(T item) {
    
    
        this.item = item;
    }
    public T get(){
    
    
        return item;
    }
}

Then create a little more fruit:

// 水果类
class Fruit {
    
    }

// 具体的水果
class Apple extends Fruit {
    
    }
class Banana extends Fruit {
    
    }

Unbounded wildcard

Using unbounded wildcards allows generics to receive any type of data. It is equivalent <? extends Object>to matching all types.

Plate<?> p1 = new Plate<>(new Fruit());
// p1.set(new Fruit());
// p1.set(new Apple());
Object p11 = p1.get();
// Fruit p12 = p1.get();
// Apple p13 = p1.get();

Plate<?> p2 = new Plate<>(new Apple());
// p2.set(new Fruit());
// p2.set(new Apple());
Object p21 = p1.get();
// Fruit p22 = p1.get();
// Apple p23 = p1.get();

Unbounded wildcard matches any class .

You can not use the setmethod. Because I don’t know how big the container inside the plate is. If the container inside the plate is relatively small, it will cause problems if you put a big thing into it, or that the container inside the plate has nothing to do with what you want to put, so even more There will be a problem;

Nor can the retrieved result be put into any class other than the Object class. Because you don't know how big the contents of the plate are, if the container you hold has nothing to do with the contents, or the size is smaller than the inside, then there will be problems. Call Objectto receive no problem, because it is the largest container, no matter what you put inside that can access is maintained.

The summary is: you can't save in, and you can't take it out.

Upper boundary wildcard

Generics using wildcards with a fixed upper boundary can receive data of a specified type and all its subclasses, where the specified type can be a class or an interface.

Plate<? extends Fruit> p3 = new Plate<>(new Fruit());
// p3.set(new Fruit());
// p3.set(new Apple());
Fruit p31 = p3.get();
Object p32 = p3.get();
// Apple p33 = p3.get();

The upper boundary character can match any derived class or the class itself with this class as the parent class .

It can not call setmethod. Because the virtual machine only know that it is put into the parent class or a derived class the parent class, a class does not know exactly which, if the original is a subclass, put the time to put the parent class, it will have a problem, so it can not call setmethod ;

But it can call getmethods, knowing that inside the container are the parent or the parent of a derived class, so take the time to find a container as long as the parent is more than equal to absolutely be installed under.

The summary is: you can't save in, only take out.

Lower boundary wildcard

The generic type of all wildcards with a fixed lower boundary can receive data of the specified type and all its superclass types.

Plate<? super Fruit> p4 = new Plate<>(new Fruit());
p4.set(new Fruit());
p4.set(new Apple());
// Fruit p41 = p4.get();
Object p42 = p4.get();
// Apple p43 = p4.get();

It can call setmethods. Because virtual machines know, the dish is the smallest container Fruitclass is so big, so when I put it in, just put something that is less than or equal to Fruitclass size, it would not be a problem;

But it can not call getmethod, because the virtual machine does not know how things inside, if you pick the wrong container will be smaller than the inside, but if you take the Objectpick would be no problem, because Objectthat is the largest of the state.

PECS principles

PECS(Producer Extends Consumer Super)原则

  • It is suitable to use the upper bound Extends if the content is read out frequently .
  • Often inserted in, suitable for the lower bound Super.

Guess you like

Origin blog.csdn.net/qq_43580193/article/details/112749414