Java泛型的使用

时间:2021-09-16 15:55:11

泛型的定义:

泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实 际的类型参数,也称为类型实参)。

泛型的引入背景:

集合容器类在设计阶段或声明阶段不能确定这个容器到底实际存储的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection ,List ,ArrayList 这个 就是类型参数,即泛型。

在集合中使用泛型

1.集合中没有使用泛型时:

1.任何类型都可以添加到集合中:类型不安全。
2.读取出来的对象需要强转:还可能会有报异常:java.lang.ClassCastException

//在集合中没有使用泛型时
@Test
public void test1(){
    ArrayList list = new ArrayList();
    list.add(66);
    list.add(55);
    list.add(77);
    list.add(59);

    //类型不安全
    // list.add("AA");

    for (Object score : list){
 //在进行向下强转时可能会出异常:java.lang.ClassCastException
        int stuScore = (int) score;
        System.out.println(stuScore);
    }
}
2.在集合中使用泛型后:

只有指定类型才可以添加到集合中:类型安全。
读取出来的对象不需要强转:便捷。
可以保证如果程序在编译时没有发出警告,运行时就不会产生 ClassCastException异常。

    //在集合中使用泛型的情况,以ArrayList为例
@Test
public void test2(){
  ArrayList<Integer> list = new ArrayList<Integer>();
  list.add(66);
  list.add(59);
  list.add(86);
  list.add(90);
  //在编译时,就会进行类型检查,保证数据的安全性。
 //      list.add("haha");

    Iterator<Integer> iterator = list.iterator();
    while(iterator.hasNext()){
        int stucore = iterator.next();
        System.out.println(stucore);
    }
}

//在集合中使用泛型的情况,HashMap为例
@Test
public void test3(){
    Map<String,Integer> map = new HashMap<String,Integer>();
    map.put("AA",66);
    map.put("BB",59);
    map.put("DD",86);
    map.put("CC",96);

    //泛型的嵌套
    Set<Map.Entry<String, Integer>> entry = map.entrySet();
    Iterator<Map.Entry<String, Integer>> iterator = entry.iterator();
    while (iterator.hasNext()){
        Map.Entry<String, Integer> entry1 = iterator.next();
        String key = entry1.getKey();
        Integer value = entry1.getValue();
        System.out.println(key + "--->" + value);
    }

}

小结:

1.在实例化集合类时,可以指明具体的泛型类型。
2.指明以后,在集合类或接口中凡是定义类或接口时,内部的结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。

  1. 泛型的类型必须是类,不能是基本数据类型,需要用到基本数据类型的位置,用包装类来替换。
  2. 如果实例化时,并没有指明泛型的类型,则默认类型为java.lang.Object类型。

    自定义泛型类

  3. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。
  4. 泛型类的构造器如下:public GenericClass(){}。
    而public GenericClass (){} 是错误的
  5. 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
  6. 泛型不同的引用不能相互赋值。
  7. 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。泛型要使用就全部都使用。要不用,就全部都不使用。
  8. 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
  9. jdk1.7以后,泛型的简化操作:ArrayList arrli = new ArrayList<>();
  10. 泛型的指定中不能使用基本数据类型,可以使用包装类替换。
  11. 在类或接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。
  12. 异常类不能是泛型。
  13. 不能使用new E[]。但是可以是:E[] elements = (E[])new Object[capacity];
  14. 父类有泛型时,子类除了指定或保留父类的泛型,还可以增加自己的泛型。

参考代码如下:
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 String getOrderName() {
        return orderName;
    }

    public void setOrderName(String orderName) {
        this.orderName = orderName;
    }

    public int getOrderId() {
        return orderId;
    }

    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }

    //不是泛型方法
    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 <E> List<E> copyFromArrayToList(E[] arr){
    ArrayList<E> list = new ArrayList<>();
    for (E e : arr){
        list.add(e);
    }
    return list;
    }
}

SubOrder.java

//SubOrder 不再是泛型类
public class SubOrder extends Order<Integer> {

    public <E> List<E> copyFromArrayToList(E[] arr){
        ArrayList<E> list = new ArrayList<>();
        for (E e : arr){
            list.add(e);
        }
        return list;
    }
}

SubOrder1.java

//SubOrder1仍然是泛型类
public class SubOrder1<T> extends Order<T> {

}

对上述方法进行测试:

 @Test
public void test1(){
    //如果定义了泛型类,实例化时没有指明类的泛型,则默认此泛型类型为Object类型
    //如果定义了类是带泛型的,建议在实例化的时候要指明类的泛型
//        Order order = new Order();
//        order.setOrderT(123);
//        order.setOrderT("abc");

       //建议,实例化时指明类的泛型
    Order<String> order = new Order<>("AA",1001,"haha");
    order.setOrderT("AA:hello");
}

@Test
public void test2(){
    SubOrder sub = new SubOrder();
    //由于子类在继承带泛型的父类时,指明了泛型类型,则实例化子类对象时,不再需要指明泛型类型
    sub.setOrderT(1111);

    SubOrder1<String> sub1 = new SubOrder1<>();
    sub1.setOrderT("AAA");
}

@Test
public void test3(){

    //泛型不同的引用不能相互赋值。
    ArrayList<String> list  = null;
    ArrayList<Integer> list1 =null;
//  list =list1;

}

//测试泛型方法
@Test
public void test4(){
   Order<String> order = new Order<>();
   Integer[] arr = new Integer[]{1,2,3,4};
   //泛型方法在调用时,指明泛型参数的类型,这个类型与类的泛型没有任何关系。
    List<Integer> list = order.copyFromArrayToList(arr);

//  Iterator<Integer> iterator = list.iterator();
//   while (iterator.hasNext()){
//     System.out.println(iterator.next());
//   }
    System.out.println(list);
}
   

通配符

通配符就是英文格式下的 ?
类A是类B的父类,G< A> 和G是没有关系的,二者的共同父类是G<?>

@Test
public void test3(){
    List<Object> list1 = null;
    List<String> list2 = null;
    List<?> list3 = null;

    list3 = list1;
    list3 = list2;

    print(list1);
    print(list2);

    //
    List<String> list4 = new ArrayList<>();
    list4.add("HH");
    list4.add("AA");
    list4.add("QQ");
    list4.add("WW");

    list3 = list4;

  //添加:对于List<?>就不能向其内部添加数据。除了可以添加null以外
//  list3.add("DD");
//   list3.add("?");
// list3.add('?');
// list3.add(null);

    //获取(读取):允许读取数据,读取的数据类型为Object。
    Object o = list3.get(0);
    System.out.println(o);
}

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