泛型的基本使用

泛型的基本使用

泛型介绍

泛型(Generic),即“参数化类型”( parameterized type)。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法

泛型类和泛型接口

接口和类差不多,所以这里偷个懒,只介绍泛型类

定义泛型类

直接在类名后加上一对尖括号,尖括号内的标识符即使泛型,在泛型类中可以把泛型当作正常的类型来使用,如果有多个泛型,则写在同一对尖括号里,泛型间用逗号隔开

public class Person<T> {

    T name;

    Person() {
    }

    Person(T name) {
        this.name = name;
    }

    public T getName() {
        return name;
    }

    public void setName(T name) {
        this.name = name;
    }
}


使用泛型类

在实例化时,类型后面加上尖括号,给泛型真实的类型,也就是说只有在实例化时,泛型的类型才真正的确定了,例如

Person<String> person = new Person<String>();

在java1.7后,最后面的String可以省略不写

注意:如果在实例化时不指定泛型的类型,那么泛型会自动变成Object,使用不太方便,所以推荐指定

也可以正常的调用泛型类中带泛型的方法:

person.setName("张三");
System.out.println(person.getName());

此时setName里参数的类型就是StringgetName的返回值也是String

泛型类有关继承的问题

主要可以分为两种形式:

  1. 子类不是泛型类:

    此时需要在继承时给父类指定泛型,子类就不需要定义泛型了

    public class Child extends Person<String> {}
    
  2. 子类也是泛型类

    此时需要给子类定义泛型,然后再把泛型指定给父类

    public class Child<E> extends Person<E> {}
    

如果两个泛型类是父子关系,且两者的指定的泛型是一样的,则其对象是可以相互赋值的,最常见的就是

List<Integer> list = new ArrayList<>();

注意事项

  1. 构造方法不需要写定义泛型

  2. 一般情况下(存在特殊情况),两个泛型对象,如果泛型类型不同(包括父子关系),就不能相互赋值,下面是错误示范

    Person<Integer> person1 = null;
    Person<String> person2 = null;
    person1 = person2;// 语法错误
    

    但是在运行时只有一个Person类被加载到JVM中

  3. 当泛型类作为一个方法的参数/返回值的类型时,就算其指定的泛型不同,也算是同一类型,不会构成重载,错误示范如下

    // 会报错,因为两个方法头重复
    public void fun(List<String> list) {}
    public void fun(List<Integer> list) {}
    
  4. 如果泛型不指定,泛型就会作为Object来处理,但不等价于Object

  5. 泛型不能用基本数据类型指定,可以使用包装类来指定

  6. 泛型不能再静态方法/属性中使用,因为泛型类只有在实例化时,泛型的类型才确定

  7. 异常类不能是泛型类,也泛型也不能指定为异常类

  8. 泛型数组不能直接初始化为new T[10],可以这样写

    T[] arr = (T[]) new Object[10];
    

泛型方法

注意:泛型方法不是在泛型类里使用了泛型类定义的泛型的方法,他跟泛型类没有任何关系,你可以在普通的类里定义泛型方法

定义泛型方法

在返回值前面加上一对尖括号定义泛型,就可以在该泛型方法中当作正常的泛型来使用了

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

使用泛型方法

如果泛型作为参数的类型,则会在调用时自动指定类型,例如

Integer[] arr = {1, 2, 3, 4};
copyFromArrayToList(arr);

此时E就是Integer

注意事项

  1. 泛型方法可以声明为static

  2. 如果泛型不作为参数的类型,也就不能为其指定类型,所以泛型会作为Object类型

  3. 泛型方法中泛型不能直接初始化为new E(),可以

    E e = (E) new Object();
    
  4. 同样不能使用基本数据类型指定泛型


通配符?

当我们把泛型类作为参数/返回值的类型时,其泛型还未指定,此时如果我们给他指定一个具体的类型,则很有可能要写许多个方法来使用泛型的类型的变化,如果不给他指定,写起来不规范,用起来也不方便,这时就需要使用通配符了,例如

public void fun(List<?> list){}

这样只要实参时List类型,无论其泛型是什么,都可以调用该方法

通配符,其可以匹配任何类型

注意事项

对于List<?>的对象,不能向其中添加数据(除了null),可以读取数据,类型是Object

也就是说,对于使用通配符?的泛型类对象,你不能再使用其参数类型是泛型的方法,因为你根本无法确定其类型,也就不知道传什么类型才行(除了null)


限制条件

限制条件常和通配符配合使用,也可以在定义泛型时使用,主要有两种

  1. ? extends A:继承于A类或者A类,即<=A
  2. ? super A:A类的父类或者A类,即>=A

注意事项

直接上代码,其中FatherChild的父类

List<? extends Father> list1 = new ArrayList<>();
List<? super Father> list2 = new ArrayList<>();
// 获取的可以是Father类型,因为list1中存放的对象类型都是Father的子类或者Father,而子类对象是可以赋值给父类对象的
Father f = list1.get(0);
// 报错,因为list1中可以存储Child的子类,而父类不能赋值给子类
list1.add(new Child());

// 报错,因为list2中可以存放Father的父类,而父类不能赋值给子类
Father f = list2.get(0);
// 因为list2中存放的对象类型都是Father的父类或者Father,而子类可以赋值给父类
list2.add(new Child());
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值