自定义泛型结构:泛型类

2020-11-22 0 By admin

自定义泛型结构包括自定义泛型类、泛型接口和自定义泛型方法;其中自定义泛型类和泛型接口几乎相同。

一、自定义泛型类介绍

jdk1.7,泛型的简化操作:ArrayList flist = new ArrayList<>();
如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。

1.1、自定义泛型类示例

泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>。
泛型类的构造器如下:public GenericClass(){}。而下面是错误的:public GenericClass<E>() {…}

public class OrderTest<T> {
  String orderName;
  int orderId;
  //类的内部结构就可以使用类的泛型
  T orderT;
  public OrderTest(){
  };
  public OrderTest(String orderName,int orderId,T orderT){
    this.orderName = orderName;
    this.orderId = orderId;
    this.orderT = orderT;
  }
  //如下的三个方法都不是泛型方法
  public T getOrderT(){
    return orderT;
  }
  public void setOrderT(T orderT){
    this.orderT = orderT;
  }
  @Override
  public String toString() {
    return "Order{" +
        "orderName='" + orderName + '\'' +
        ", orderId=" + orderId +
        ", orderT=" + orderT +
        '}';
  }
  //此为泛型方法
  public static <E>  List<E> copyFromArrayToList(E[] arr){
    ArrayList<E> list = new ArrayList<>();
    for(E e : arr){
      list.add(e);
    }
    return list;
  }
}
public class SubOrder extends OrderTest<Integer>{ 
//SubOrder:不是泛型类
}
public class SubOrder1<T> extends OrderTest<T> {
//SubOrder1<T>:仍然是泛型类
}
import org.junit.Test;
public class GenericTest1 {
  @Test
  public void test(){
//如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object类型。
//泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。
//经验:泛型要使用一路都用。要不用,一路都不要用。
//要求:如果大家定义了类是带泛型的,建议在实例化时要指明类的泛型。
    OrderTest order = new OrderTest();
    order.setOrderT(123);
    order.setOrderT("ABC");
//建议:实例化时指明类的泛型
//实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。此次为String
    OrderTest<String> order1 = new OrderTest<String>("orderAA",1001,"order:AA");
    order1.setOrderT("AA:hello");
  }

  @Test
  public void test2(){
    SubOrder sub1 = new SubOrder();
    //由于子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再需要指明泛型。
    sub1.setOrderT(1122);
    SubOrder1<String> sub2 = new SubOrder1<>();
    sub2.setOrderT("order2...");
  }
}

二、泛型类和泛型接口使用注意点

2.1、泛型类型不同的同类型引用不能相互赋值

虽然 ArrayList<String> 和 ArrayList<Integer> 两个泛型集合的对象,在运行时变量类型都是 ArrayList类型;
但是在编译过程中它们是两个类型;所以不能对这两个对象相互赋值,在编译过程中会报错。

  1. 泛型只在编译阶段有效,在编译之后程序会采取去泛型化的措施。
  2. 也就是说Java中的泛型,只在编译阶段有效。
  3. 在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦除,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。
  4. 也就是说,泛型信息不会进入到运行时阶段。
  5. 对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。

2.2、异常类不能是泛型的

//异常类不能声明为泛型类
public class MyException<T> extends Exception{
}
public void show(){
//编译不通过
   try{
   }catch(T t){
   }
}

2.3、泛型类中实例化泛型对象

不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];

public OrderTest(){
//编译不通过
//  T[] arr = new T[10];
//编译通过
    T[] arr = (T[]) new Object[10];
};

参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。

2.4、静态方法中不能使用类的泛型

public static void show(T orderT){
  System.out.println(orderT);
}

在 Java中泛型只是一个占位符,必须在传递类型后才能使用。
就泛型而言,类实例化时才能正真的的传递类型参数;由于静态方法的加载先于类的实例化,也就是说类中的泛型还没有确定得类型参数,静态的方法就已经加载完成了。

2.5、在继承中使用泛型

父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:

class Father<T1, T2> {}
//子类不保留父类的泛型
// 1)没有类型擦除
class Son1 extends Father {
// 等价于class Son extends Father<Object,Object>{}
}
// 2)具体类型
class Son2 extends Father<Integer, String> {}
// 子类保留父类的泛型:泛型子类
// 1)全部保留
class Son3<T1, T2> extends Father<T1, T2> {}
// 2)部分保留
class Son4<T2> extends Father<Integer, T2> {}

结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型。