基础——泛型

概述

集合可以存储任意类型的对象,对象存入集合以后都被提升为Object类型。当我从集合中取出对象的时候都需要进行强制转型来后续操作对象。

 public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("a11");
        list.add(1);
        list.add("1");
        for (Object o : list) {
			String str = (String) o;
            System.out.println(str);
        } 
    }

这段简单的演示代码就会出现题java.lang.ClassCastException的运行时期异常。通常遇到这种情况可以使用instanceof来判断是否为某种类型或是其子类型,但这样会增加代码复杂度,且不能彻底解决问题,毕竟再怎么考虑终会有所遗漏的类型不能完成正常转换。
JDK1.5之后引入了**泛型(Generic)**语法。这样我们在使用设计API的时候可以指定类或者方法甚至接口的泛型,设计出的API也更为简洁,并且也能在编译时期查出异常。

使用泛型的好处

  1. 避免强制类型转换问题
  2. 将运行时异常提前到编译时期,减少了后期修改bug的工作量
  3. 一旦指定泛型,数据类型将被统一
  4. 实现代码的模块化,把数据类型当做参数

泛型的定义和使用

1、泛型类的定义

(1)格式

public class 类名<泛型变量>{
	...
}

泛型类中的泛型一旦被指定,整个类除了泛型方法,全部带有泛型的类型占位符的地方都会被替换为指定的类型。

(2)使用范例

public class MyClass<T> {
    private T t;

    public MyClass() {
    }

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

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}
	public static void main(String[] args) {
        // 空参构造创造对象
        MyClass<String> mc1 = new MyClass<>();
        mc1.setT("Hello");
        String t = mc1.getT();
        System.out.println(t);
        System.out.println("-----------------------------------");
        // 满参构造,尖括号指定了什么类型就传递什么类型数据
        MyClass<Integer> mc2 = new MyClass<>(1);
        String t = mc1.getT();
        System.out.println(t);
    }

2、泛型方法的定义

在泛型类使用中一般默认直接把泛型占位符替换为统一指定的类型,但有时候需要方法定义自己特定的泛型。

(1)格式

修饰符 <T> 返回值类型 方法名称(T t,参数列表...){
	方法体...
}

注意事项:

  1. 前面<T>在定义方法的泛型
  2. 后面方法参数:T t 使用的是方法自己的的泛型

(2)使用范例

public class MyClass02<T> {
    // 不是泛型方法
    public void print(T t){
        System.out.println(t);
    }
	// 泛型方法,定义了自己的泛型,使用占位符E表示泛型
    public <E> void show(E e){
        System.out.println(e);
    }
}
public static void main(String[] args) {
        MyClass02<String> mc = new MyClass02<>();
        mc.print("Hello");
        // print 使用的是类上的泛型,已经确定为String类型了
        //错误:mc.print(10000);
        mc.show(18);
    }

3、泛型接口的定义

(1)格式

public interface 接口名称<T> {
	...
}

(2)接口上定义的泛型,什么时间确定具体类型

  1. 定义实现类时,直接确定接口上泛型的具体类型
public interface MyInter<T> {
    public abstract void show(T t);
}
/*
    定义实现类时,直接确定接口上泛型的具体类型
 */
public class MyInterImpl implements MyInter<String> {
    @Override
    public void show(String s) {
        System.out.println(s);
    }
}

  1. 定义实现类时不确定接口上泛型的具体类型,那么该实现类必须定义为泛型类,而且实现类上的泛型变量要和接口上的泛型变量名称一致——创建实现类对象时,<>中要写是什么类型,泛型就是什么类型。这也叫做泛型的传递
public interface MyInter<T> {
    public abstract void show(T t);
}
/*
定义实现类时不确定接口的类型
*/
public class MyInterImplB<T> implements MyInter<T> {
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

泛型的通配符

代表任意的一种引用类型,只能用来匹配泛型,不能用来定义泛型。

(1)注意事项

  1. 泛型是不存在多态的,创建集合对象时,左右两边的<>内容要保持一致
  2. ArrayList< ? > list 可以接受什么?
    可以接受ArrayList的任意类型对象(只要在创建ArrayList集合对象中<>写上一种引用类型,都是可以的)

(2)示例

	public static void main(String[] args) {
        ArrayList<String> l1 = new ArrayList<>();
        Collections.addAll(l1,"123-123qwe-qwe-qwe-fhj-ghj-yjw-wlt-gfd-iop".split("-"));
        ArrayList<Integer> l2 = new ArrayList<>();
        Collections.addAll(l2,4,5,6,13,123,123,123,45,6,7,12,890,7,2,3,30);
        print(l1);
        print(l2);
    }

    public static void print(ArrayList<?> arrayList){
        System.out.println(arrayList);
    }

泛型的上下限

使用泛型的通配符,我们可以接受任何类型的参数,但有时候我们需要更加严格的限制传递的类型,比如,我们需要传递的参数是某个类型或者它的子类,又或是某个类型或者它的父类。此时,我们就需要使用泛型的上下限来做约束。

1、泛型的上限:

格式-类型名称<? extends E> :表示E类型或者E类型任意子类

2、泛型的下限:

格式-类型名称<? super E>:表示E类型或者E类型的任意父类

猜你喜欢

转载自blog.csdn.net/weixin_38708854/article/details/106850355
今日推荐