泛型和包装类

泛型和包装类

一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。而泛型出现的作用就是使其具备多种类型(类型进行参数化

例如:若你的方法参数是一个泛型的数组,那么你在调用方法的时候,不管你传递什么类型的参数它都可以进行接收

引入泛型

实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值?

思路:

  1. 我们以前学过的数组,只能存放指定类型的元素
  2. 我们学过继承后,就知道所有的类的父类都是 Object,那么我们可以使用Object来定义数组吗?
class MyArray {
    public Object[] array = new Object[10];

    public Object getPos(int pos) {
        return this.array[pos];
    }
    public void setVal(int pos,Object val) {
        this.array[pos] = val;
    }

}

public class Demo {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.setVal(0,1);
        myArray.setVal(1,"hello");

        int a = (int)myArray.getPos(0);
    }
}

上述代码存在的问题:

  • 任何类型都可以存放,导致在取元素的时候,必须进行强制类型转换(这就大大降低了程序员的编码效率)

虽然在这种情况下,当前数组任何数据都可以存放,但是,更多情况下,我们还是希望他只能够持有一种数据类

型。而不是同时持有这么多类型。所以,泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译

**器去做检查。**此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。

泛型语法

  1. ,就指定了当前数组中只能存放Integer类型的数据
  2. 泛型在编译期间帮我们做了两件事
    1. 存元素的时候进行类型检查
    2. 取元素的时候,进行类型强转
  3. 泛型的 <> 中只能放 引用类型,不能放置基本类型(Java给我们的基本类型都提供了对应的包装类了)
class MyArray1<T> {
    // 理论上不可以这样定义泛型数组,需要用到反射(但是Java源码中都是直接使用Object[] 数组的,都没用泛型数组)
    public T[] array = (T[])new Object[10];   

    public T getPos(int pos) {
        return this.array[pos];
    }
    public void setVal(int pos,T val) {
        this.array[pos] = val;
    }

}

public class Demo1 {
    public static void main(String[] args) {
        MyArray1<Integer> myArray = new MyArray1();
        myArray.setVal(0,1);
        int a = myArray.getPos(0);

        MyArray1<String> myArray1 = new MyArray1<>();
        myArray1.setVal(0,"hello");
        myArray1.getPos(0);
    }
}

解释:

  1. 类名后的 代表占位符,表示当前类是一个泛型类

    • E 表示 Element

    • K 表示 Key

    • V 表示 Value

    • T 表示 Type

// 解释:若能实例化成功的话,就表示T[]能够作为方法参数进行返回,而T[]中能存放任何类型的数据;这个时候外部进行接收此方法返回值的时候,就无法进行接收(强转也不行)  这也属于Java的一个安全机制吧

public T[] array = new T[5];   // 这个为啥会报错?

image-20221013113839553

擦除机制

泛型在编译的时候,其中的泛型都会进行擦除为 Object

image-20221013114113239

有关泛型擦出机制的文章介:https://zhuanlan.zhihu.com/p/51452375

泛型的上界

T extends Comparable :用extends来指定泛型的上界----> 擦除机制默认是直接擦除到Object类的,而Object类并没有实现Comparable接口,就无法进行引用类型的比较了;extends 就可以指定擦除到什么类,就不往下进行擦除了(这里就是擦除到实现了Comparable接口的类,就不往下了,这样我们就可以使用 comparaeTo 方法进行比较了)

// 写一个泛型类,类中有个方法,求数组的最大值
class Alg<T extends Comparable<T>> {
    public T findMax(T[] array) {
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(max.compareTo(array[i]) < 0) {
                max = array[i];
            }
        }
        return max;
    }
}

public class Demo2 {
    public static void main(String[] args) {
        Alg<Integer> alg = new Alg<>();
        Integer[] array = {1,2,2,6,7,3,1,10};
        System.out.println(alg.findMax(array));
    }
}

// 表示Test里面指定的数据类型只能是Number以及Number的子类
class Test<T extends Number> {
    
}
// 静态写法
// 静态的
class Alg {
    public static<T extends Comparable<T>> T findMax(T[] array) {
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(max.compareTo(array[i]) < 0) {
                max = array[i];
            }
        }
        return max;
    }
}

public class Demo2 {
    public static void main(String[] args) {
        Integer[] array = {1,2,2,6,7,3,1,10};
        System.out.println(Alg.<Integer>findMax(array));
        System.out.println(Alg.findMax(array));   // 根据array里面的内容,自动推导类型
    }
}

通配符

用来解决泛型无法进行 协变 的问题的

一般用在源码中,了解即可

class Message<T> {
    private T message ;
    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }
}

public class Demo3 {
    public static void main(String[] args) {
        Message<String> message = new Message() ;
        message.setMessage("你好");

        Message<Integer> message1 = new Message() ;
        message1.setMessage(1);

        fun(message);
        fun(message1);
    }

    // 此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,所以无法修改
    public static void fun(Message<?> temp){
	// temp.setMessage("hello");  // 无法进行
        System.out.println(temp.getMessage());
    }
}

image-20221013132647834

上界

// 一般用来读取元素
// 这里可以接收Fruit以及它的子类
public static void fun(Message<? extends Fruit > temp){
    // 这里传过来的最大的就是Fruit了,所以只能用来获取,不能进行存放
    System.out.println(temp.getMessage());
    Fruit fruit = new Apple();
}

下界

// 一般用来设置元素
// 这里可以接收Fruit以及它的父类,这里进行存放,只能存放Fruit以及它的子类
public static void fun(Message<? super Fruit > temp){
    // 这里最低传过来的都是Fruit,那么Fruit肯定可以存放它的子类的(而Fruit父类就不能进行set了)
    temp.setMessage(new Apple());
    temp.setMessage(new Banana());
}

包装类

Java中将基本数据类型封转成的类,称作 包装类;如:Integer,Character,Byte……

拆包与装包

public static void main(String[] args) {
        Integer a = 10;
        int b = a;  // 自动拆包
        double d = a; // a是什么类型,调用什么类型的拆包方法,这里调用的是intValue
        double d2 = a.doubleValue(); // 手动拆包
    }

    public static void main1(String[] args) {
        int a = 10;
        Integer b = a;  // 自动装包(底层自动调用valueOf)
        Integer c = Integer.valueOf(a); // 手动装包
    }

注意点

// 看下图,若 values处于 -128 ~ 127 之间,直接进行返回;而不在这个范围的会进行 new 对象(所以以后我们进行引用对象比较的时候,还是建议用eqals来比较值)
public static void main(String[] args) {
    Integer a = 100;
    Integer b = 100;
    System.out.println(a == b);  // true
}
    
public static void main3(String[] args) {
    Integer a = 200;
    Integer b = 200;
    System.out.println(a == b);  // false
}

ublic static void main(String[] args) {
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true
}

public static void main3(String[] args) {
Integer a = 200;
Integer b = 200;
System.out.println(a == b); // false
}


![image-20221013133803547](https://img-blog.csdnimg.cn/img_convert/c03f002e9c222a8c76f175738830e7e1.png)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值