泛型

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2022-06-25

基本使用

把元素的类型设计成一个参数,这个类型参数就是泛型。

import org.junit.Test;

import java.util.*;

public class GenericTest {

  @Test
  public void test1() {
    // 必须是包装类,而不能是基本数据类型 int
    ArrayList<Integer> list = new ArrayList<Integer>();

    // 后面的泛型参数一样 可以省略
    // ArrayList<Integer> list = new ArrayList<>();

    list.add(123);
    list.add(456);
    list.add(789);
    // list.add("123"); // 编译时就会报错

    for (Integer number: list) {
      System.out.println(number);
    }

    Iterator<Integer> iterator = list.iterator();

    while(iterator.hasNext()) {
      int n = iterator.next(); // 直接就能用int来接收
      System.out.println(n);
    }

    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("jack", 123);
    // map.put("jack", "male"); // 编译就会报错

    // 泛型的嵌套
    Set<Map.Entry<String, Integer>> entries = map.entrySet();
    Iterator<Map.Entry<String, Integer>> iterator1 = entries.iterator();

    while(iterator1.hasNext()) {
      Map.Entry<String, Integer> next = iterator1.next();

      String key = next.getKey();
      Integer value = next.getValue();
      System.out.println(key + "===>" + value);
    }
  }
}

自定义泛型结构

// Order.java
public class Order<T> {

  String orderName;
  int orderId;

  // 上面添加了类型参数,在类的内部结构中就能使用类的泛型
  T orderT;

  public Order() {};

  public Order(String orderName, int orderId, T orderT) {
    this.orderName = orderName;
    this.orderId = orderId;
    this.orderT = orderT;
  }

  public T getOrderT() {
    return this.orderT;
  }

  public void setOrderT(T orderT) {
    this.orderT = orderT;
  }

  @Override
  public String toString() {
    return "Order{" +
            "orderName='" + orderName + '\'' +
            ", orderId=" + orderId +
            ", orderT=" + orderT +
            '}';
  }
}

// GenericTest2.java

import org.junit.Test;

public class GenericTest2 {
  @Test
  public void test1() {
    // 如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object类型 所以建议如果定义了实例化是就应该使用
    Order order = new Order();
    order.setOrderT(123);
    order.setOrderT("ABC");

    Order<String> order1 = new Order<>();

    Order<String> order2 = new Order<>("orderA", 1, "服装订单");
    order2.setOrderT("汽车订单");
    System.out.println(order2.getOrderT());
  }

  @Test
  public void test2() {
    SubOrder sub1 = new SubOrder();
    // 由于子类SubOrder在继承带泛型的父类时,指明了泛型类型  public class SubOrder extends Order<Integer> {}
    // 则实例化子类对象时,不需要在指明泛型
    sub1.setOrderT(123);


    // public class SubOrder1<T> extends Order<T> {}
    SubOrder1<String> subOrder1 = new SubOrder1<>();
    subOrder1.setOrderT("123");
  }
}

需要注意的点:

  • 泛型类可能有多个参数,此时可以将多个参数一起放在尖括号内,比如 Order<T, K, E>

  • 泛型类的构造器如下:public Order() {} 而不是 public Order<T>() {}

  • 泛型不同的引用不能相互赋值

@Test
public void test() {
  Person p1 = null;
  Person p2 = null;
  p1 = p2; // 这样是可以的

  ArrayList<String> list1 = null;
  ArrayList<Integer> list2 = null;

  list1 = list2; // 这样编译就会报错
}
  • 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等于Object。

  • 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。

  • 泛型的指定中不能使用基本数据类型,可以使用包装类替代。

  • 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型,但在静态方法中不能使用类的泛型。

public class Order<T> {
  T orderT;

  public Order() {};

  public Order(T orderT) {
    this.orderT = orderT;
  }

  public static void show() {
    // 在静态方法的方法体里面访问会报错
    System.out.println(orderT);
  }

  // 在静态方法的参数里面使用也会报错
  public static void say(T orderT) {
    System.out.println(orderT);
  }

  // 因为静态方法会早于类加载的时候加载,此时泛型还没有指定,泛型会在类的对象创建的时候指定,
  // 所以在静态方法中不能使用类的泛型

  // 上面的都不是泛型方法,泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系
  // 如果是一个泛型方法,那么是可以使用泛型的,因为它会在方法调用的指定。
  // 见下面 泛型方法 部分的示例
}
  • 异常类不能声明为泛型类

  • 子类和父类的泛型继承

class Father<T1, T2> {}


// 子类不保留父类的泛型
// 1、没有类型
class Son1 extends Father {}
// 等价于 class Son extends Father<Object, Object> {}

class Son1<A, B> extends Father {}
// 等价于 class Son<A, B> extends Father<Object, Object> {}


// 2、具体类型
class Son2 extends Father<Integer, String>{} 
class Son2<A, B> extends Father<Integer, String>{} 


// 子类保留父类的泛型
// 1、全部保留
class Son3<T1, T2> extends Father<T1, T2> {}
class Son3<T1, T2, A> extends Father<T1, T2> {}


// 2、部分保留
class Son4<T2> extends Father<Integer, T2> {}

class Son4<T2, A> extends Father<Integer, T2> {}
  • 类A是类B的父类,T<A>T<B>二者不具备子父类关系,二者是并列关系。但A<T>B<T>是父子类关系,例如 List<String> list = new ArrayList<String>(); 这样的赋值操作。有的场景需要封装方法技能处理T<A>也能处理T<B>,此时可以使用?通配符来解决,T<?>就是T<A>T<B>共同的父类。
import org.junit.Test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class GenericTest2 {

  @Test
  public void test() {
    List<Object> list1 = new ArrayList<>();
    List<String> list2 = new ArrayList<>();

    List<?> list = null;

    list = list1;
    list = list2;

    list1.add(123);
    list2.add("123");

    
    list = list2;
    // list.add("AA"); // 编译报错 error
    // list.add(null); // 编译通过,但是会报错,因为上面设值list本质还是null不是一个ArrayList
    // 对于List<?> list 就不能向其内部添加数据,但是除了null除外

    Object o = list.get(0); // 编译通过 可以读取数据,读取的类型是Object

    print(list1);
    print(list2);
  }

  public void print(List<?> list) {
    Iterator<?> iterator = list.iterator();
    while(iterator.hasNext()) {
      Object next = iterator.next();
      System.out.println(next);
    }
  }


  /*
  * 有限制的统配符的使用
  * ? extends Perosn 
  * ? super Person
  */
  @Test void test4() {
    // 假设存在 Person Student BoyStudent 为 继承关系
    List<? extends Person> list1 = null;
    List<? super Person> list2 = null;

    List<Student> list3 = null;
    List<Person> list4 = null;
    List<Object> list5 = null;

    list1 = list3;
    list1 = list4;
    // list1 = list5; // error extend要求必须继承于或者等于Person

    // list2 = list3; // error super要求必须是Person的父类或等于Person
    list2 = list4;
    list2 = list5; 

    list1 = list3;
    Person map = list1.get(0); 
    // 可以不用是Object,因为约束最“大”的类就是Person了

    list2 = list4;
    Object o = list2.get(0); 
    // 这里要用Object,因为list2约束最小的类就是Person,可能会比它大,所以不能用Person来接收


    // 写入数据
    // 编译不通过,因为extend约束要小于Person
    // 那如果Student的子类BoyStudent更小于Person,显然可以有
    // List<BoyStudent> boys 也能赋值给list1
    // 此时如果add一个new Student() 就是父类赋值给子类,所以不允许
    list1.add(new Student());

    // list2的约束,必须是Person或者Person的父类
    // 所以Person是最小的,即使List<?>是大于Person的父类,也能add,因为也是属于子类赋值给父类
    // 既然Person 那么Person的子类也就更可以了
    list2.add(new Person());
    list2.add(new Student());
  }
}

泛型方法

// 泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系
// 可以声明成static
public <E> List<E> copyFromArrayToList(E[] arr) {
  ArrayList<E> list = new ArrayList<>();

  for(E e: arr) {
    list.add(e);
  }
  return list;
}

@Test
public void test() {
  Order<String> order = new Order<>();

  Integer[] arr = new Integer[]{1, 2, 3 ,4};
  List<Integer> integers = order.copyFromArrayToList(arr);
  System.out.println(integers);
}