Android进阶学习(1)-- Java泛型

为什么需要泛型

  1. 适用于多种数据类型执行相同的代码。如:一个求和方法,参数可能是intfloatdouble不使用泛型则需要多个重载方法
  2. 泛型中的类型在使用时指定,不需要强制类型转换。如:List在使用时传入类型,在调用get(i)时直接返回对应类型数据。

泛型类的定义(类、接口)

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

1. 泛型类的定义
// T 为指定的类型,需要在使用时传入
//也可以指定多个类型 在<>中用 , 隔开
//public class Custom<T,K> {
public class Custom<T> {

    private T data;

    //泛型类和普通类一样 构造方法 get set 方法都没有问题
    public Custom(T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}
2. 泛型接口的定义

泛型接口的定义和泛型类基本相同

public interface ICustom<T> {
    public T success();
}

在实现泛型接口时,他的实现类有两种方式
(1)实现类不指定类型 在使用时传入类型

public class ImplCustom<T> implements ICustom<T> {
    @Override
    public T success() {
        return null;
    }
}
//使用时传入类型
ImplCustom<String> implCustom = new ImplCustom<>();
implCustom.success();

(2)实现类指定类型

public class ImplCustom2 implements ICustom<String> {

	//注意 这里方法的返回参数不再是 T 而是传入的 String
    @Override
    public String success() { 
        return null;
    }
}
//使用时和普通的类 没有什么区别
ImplCustom2 implCustom2 = new ImplCustom2();
implCustom2.success();

泛型方法辨析,限定类型变量

1. 泛型方法的辨析
class Custom {
    //这是一个泛型方法
    public <T> T onSuccess(){
        return null;
    }
}

class Custom2<T>{
    //这不是泛型方法! 这不是泛型方法! 这不是泛型方法!
    //这只是类成员中一个普通方法
    //他的返回值类型是在声明Custom2这个类的时候已经声明过的泛型
    public T onError(){
        return null;
    }
}
2.限定类型变量

泛型可以对类型变量加以约束,比如:计算两个变量的最大,最小值

	//如果直接写<T> 则后面调用compareTo方法会报错
    //写成<T extends Comparable> 则说明 T 一定是实现Comparable接口的类型
    //extends 在这里意味 实现、继承
    public <T extends Comparable> T getMax(T p1, T p2){
        if(p1.compareTo(p2) > 0){
            return p1;
        }else {
            return p2;
        }
    }
    //调用时
    Custom custom = new Custom();
    custom.<Integer>getMax(2,5);
    //由于Java语言特性 1.6版本后调用泛型方法可以省去类型声明,直接传入符合限定规则的类型即可
    custom.getMax(0.2,3d)

当然,在限定类型时,可以有多个条件但是只可以限定一个类,接口可以是多个,类必须写在第一位!!!

//以上个方法为例,限定类型多个条件,必须继承ArrayList类,且实现 Comparable、Serializable接口
public <T extends ArrayList & Comparable & Serializable> T getMax(T p1, T p2){
	...
}

限定条件可以是多个,当然泛型的类型也可以是多个

//代表 T 和 K 都要满足限定条件
public <T,K extends ArrayList & Comparable & Serializable> T getMax(T p1, K p2){
	...
}
//代表 T 要满足限定条件,而 K 则不需要满足限定条件
public <T extends ArrayList & Comparable & Serializable , K> T getMax(T p1, K p2){
	...
}

在泛型方法上的限定类型,同样在泛型方法,泛型接口上使用都是一样的。

泛型中的约束和局限性

1.不能实例化类型变量

比如在构造方法中 new 一个 T 类型,是会报错的在这里插入图片描述

2.静态域或者静态方法不能引用类型变量,静态方法本身是泛型方法则可以使用

因为在虚拟机执行时,static是首先执行的,类还没有加载,虚拟机根本不知道你定义的泛型 T 是什么;
在这里插入图片描述
如果一个静态方法是泛型方法,则没有任何问题
在这里插入图片描述

3.泛型类型不可以是基本类型,必须使用包装类型

基础类型在Java虚拟机中不是对象,所以必须使用包装类型
在这里插入图片描述

4.不可以使用 instanceof 关键字

在这里插入图片描述

5.不能创建参数化类型的数组

在这里插入图片描述

6.不能继承Exception、不能捕获泛型类的实例

在这里插入图片描述
在这里插入图片描述

7.捕获异常的技巧
//这样写是没问题的,可以抛出泛型异常
class Custom<T>{
            public <T extends Throwable> void getError(T t) throws Throwable{
                try{
                }catch (Exception e){
                    throw t;
                }
            }
        }

泛型类型的继承规则

我们创建两个普通类CarBaoMa 和 一个泛型类 Custom<T>
BaoMa 继承自 Car 这两个类具有继承关系
那么 Custom<Car>Custom<BaoMa>
在这里插入图片描述
但是,泛型类可以继承或者扩展其他泛型类(List 和 ArrayList就是很好的例子)

	class Custom<T>{ }

    class Test<T> extends Custom<T>{ }
	//这样的继承关系 是没有任何问题的
    Custom<String> custom = new Test<>();

鉴于Custom<Car>Custom<BaoMa> 无法实现继承关系带来很多不便,由此通配符类型就出现了

通配符类型

首先 我们建立如下的继承关系 依照这个继承关系 来进行学习

	/**
     *  类的继承关系
     *         Food
     *          ↓
     *        Fruit
     *    -------------
     *    ↓           ↓
     *  Apple        Orange
     *    ↓
     *  HongFuShi
     */
    class Food{}

    class Fruit extends Food{ }

    class Apple extends Fruit{ }

    class Orange extends Fruit{ }

    class HongFuShi extends Apple{ }

再创建一个泛型类

class Custom<T>{
        
        public T data;

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }
    }

现在我们有这样一个方法

public static void onShow(Custom<Fruit> fruitCustom){
	System.out.print(fruitCustom.getData());
}

由上节中的 Custom<Car>Custom<BaoMa>可得知 Custom<Apple> 类型无法作为参数传递到上面的onShow方法中,是因为Custom<Apple>Custom<Fruit>没有继承关系
在这里插入图片描述
那么我们对onShow方法进行修改,参数修改为通配符类型则可以解决这一问题

//将参数类型Custom<Fruit> 改为 Custom<? extends Fruit>
public static void onShow(Custom<? extends Fruit> fruitCustom){
        System.out.print(fruitCustom.getData());
}

再次调用 onShow方法 则不会报错
在这里插入图片描述
<? extends Fruit> 代表的是可传入参数类型的上界,所谓上界就是传入的类型最高只能是Fruit类型,我们来测试一下,有开头的继承关系可得知,Food类是Fruit的父类,已经超过限定的上界
在这里插入图片描述
我们可以看到,Custom<Orange>类型传入,依然没有报错,因为Orange也是Fruit的子类,并没有超过上界,而Custom<Food>类型则报错,因为超过了规定的上界!

当然有上界,就有下界,我们的类有Food,Fruit,Apple,Orange,HongFuShi。如果我现在想把限定条件改为只允许Food,Furit,Apple呢?
这时就该使用下界——<? super Class>
将onShow方法改为如下:

public static void onShow(Custom<? super Apple> fruitCustom){
        System.out.print(fruitCustom.getData());
}

再次调用onShow方法会发现只有Custom<Orange>Custom<HongFuShi>两个类型的报错。
在这里插入图片描述
这就是因为<? super Class>代表的是下界,传入的类型可以是限定类型的父类。

总结一下:
<? extends Class> 表示传入的类型限定条件的上界
<? super Class> 表示传入的类型限定条件的下界
通配符限定只能在泛型方法中使用!泛型类和接口中是不能使用的,这点就自己尝试下吧。

虚拟机是如何实现泛型的

Java虚拟机实现泛型,其实是一个泛型擦除;如:Custom<T> 再JDK中会擦除变成Custom<Object>;如果 T 有限定条件,如:Custom<T extends ArrayList & Comparable & Serializable> 会擦除成为 Custom<ArrayList > ,一般都会取限定条件中第一个类型,但是,如果在某个地方用到了Comparable或者Serializable的方法,虚拟机会在合适的时机,对 Custom<ArrayList > 加入强制类型转换代码(Comparable)Custom<ArrayList>,这里只是举例说明,实际操作中会在对应的代码前加入强制类型转换。(JDK转字节码时,会存在一个Signature弱记忆的属性,会记录原始泛型)

类型的擦除,在List使用时能够体现出来
在这里插入图片描述
看上去是两个重载方法,他们的参数类型不同,但是编译器会报错,这就说明虚拟机在实现泛型时,本质上就是类型的擦除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值