【数据结构】_2.包装类与泛型

目录

1. 包装类

1.1 基本数据类型和对应的包装类

1.2 (自动)装箱和(自动)拆箱

1.2.1 装箱与拆箱

1.2.2 自动装箱与自动拆箱

1.3 valueOf()方法

2. 泛型类

2.1 泛型类与Object类

2.2 泛型类语法

2.3 泛型类示例

2.4 裸类型

2.5 泛型类的编译

2.5.1 擦除机制

2.5.2 泛型类型数组的实例化

2.6 泛型的上界

2.6.1  N为接口

 2.6.2   Number为类

3. 泛型方法

3.1 语法

3.2 泛型方法示例

3.2.1 普通类的普通泛型方法

3.2.2 普通类的静态泛型方法


1. 包装类

在Java中由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型;

1.1 基本数据类型和对应的包装类

基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

除了Integer和Character,其余基本类型的包装类都是首字母大写;

1.2 (自动)装箱和(自动)拆箱

1.2.1 装箱与拆箱

装箱(装包):把基本数据类型变为对应的包装类型;

拆箱(拆包):把引用类型变为基本数据类型;

1.2.2 自动(显式)装箱与自动(显式)拆箱

代码示例1:自动装箱与显式装箱:

        int a = 10;
        // 自动装箱
        Integer val = a;
        System.out.println(val);
        // 显式装箱
        Integer val2 = Integer.valueOf(a);
        Integer val3 = new Integer(a);
        System.out.println(val2);
        System.out.println(val3);

 代码示例2:自动拆箱:

        Integer val1 = 10;
        // 自动拆箱
        int a = val1;
        System.out.println(a);
        // 显式拆箱
        // 拆为int类型
        int a2 = val1.intValue();
        System.out.println(a2);
        // 拆为double类型
        double a3 = val1.doubleValue();
        System.out.println(a3);

 注:(1)自动装箱的底层逻辑:Integer包装类的valueOf()方法: 

(2)自动拆箱的底层逻辑:Integer包装类的intValue()方法:

(3)显式拆箱时,Integer包装类不止提供了intValue()方法,还提供了doubleValue()等方法:

可以根据所需的基本类型对应调用其方法,在上文示例中,输出结果为:

1.3 valueOf()方法

试运行以下代码:

        Integer a = 127;
        Integer b = 127;
        System.out.println(a==b);
        Integer c = 128;
        Integer d = 128;
        System.out.println(c==d);

输出结果为:

原理注解:

(1)Integer包装类的valueOf方法源代码与方法内部相关量的值:

(2)方法含义:

如果i在[low, high]即[-128, 127]之间,则返回cache缓存数组中下标为i+128位置的元素:

如i=0时存储在cache[128],i=-128时则存储在cache[0],i=127时则存储在cache[255];

即在缓存数组中存储了256个数字;

在该范围内的i调用valueOf方法调用到的是同一个元素,故而a=b;

在该范围外的i调用valueOf方法会new Integer对象,作为引用类型,==判断的是引用类型是否是对一个对象,故而a!=b;

2. 泛型类

一般的类和方法只能使用具体的类型:基本类型或自定义类,泛型就是适用于多种类型,即对参数实现了参数化;

2.1 泛型类与Object类

现要求实现一个不限制元素种类的数组,联想到Object类,示例代码如下:

class MyArray{
    public Object[] obj = new Object[3];
    public Object getPos(int pos){
        return obj[pos];
    }
    public void setPos(int pos,Object val){
        obj[pos]=val;
    }
}
public class Demo1 {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.setPos(0,10);
        myArray.setPos(1,"hello");
        myArray.setPos(2,10.0);
        double ret = myArray.getPos(2);
        System.out.println(ret);
    }
}

报错如下:

 如要创建double类型变量对数组元素进行接收,则需要进行强转:

double ret = (double)myArray.getPos(2);

上文设计的数组具有以下特点:

① 任何数据都可以存储; ② 获取数据时必须进行强制类型转换;

这并非我们需要实现的泛型数组,通常需要实现的泛型数组需求是:存储一种类型的数据;

泛型编程是将类型作为参数传递的编程方式,目的是指定当前容器后可以存放某一种类型的数据;

2.2 泛型类语法

// 泛型的一般使用方法
class 泛型类名称<类型形参列表>{
}
class ClassName<T1,T2...Tn>{
}

// 泛型类的继承
class 泛型类名称<类型形参列表> extends 继承类{
}
class ClassName<T1,T2,...Tn> extends ParentClass<T1>{
}

注:① 类名后的<T>代表占位符,表示当前类是一个泛型类,

类型形参一般使用一个大写字母表示,常用名称有:

E表示Element,K表示Key,V表示Value,N表示Number,T表示Type;

泛型当中不能实例化一个泛型类型的数组,如:

T[] ts = new T[5];

是错误的;

③ 泛型类实例化的同时会指定当前泛型类的指数参数类型,如果二者冲突,就会报错,

这也是泛型编程的一个意义所在:泛型编程在程序编译时,存储数据时自动进行类型检查; 

2.3 泛型类示例

class MyArray<T>{
    public T[] obj = (T[])new Object[3];
    public T getPos(int pos){
        return obj[pos];
    }
    public void setObj(int pos, T val){
        obj[pos]=val;
    }
}
public class Demo1 {
    public static void main(String[] args) {
        // 实例化对象的同时指定当前泛型类的指定参数类型
        // 指定参数的类型必须是引用类型,如int非法而Integer合法
        MyArray<Integer> myArray1 = new MyArray<Integer>();
        myArray1.setObj(0,10);
        myArray1.setObj(1,78);
        myArray1.setObj(2,66);
        double ret1= myArray1.getPos(1);
        System.out.println(ret1);
        System.out.println("-------------------");
        MyArray<String> myArray2 = new MyArray<String>();
        myArray2.setObj(0,"hello");
        myArray2.setObj(1,"world");
        myArray2.setObj(2,"java");
        String ret2 = myArray2.getPos(1);
        System.out.println(ret2);
    }
}

输出结果为:

2.4 裸类型(Raw Type)

裸类型是一个泛型类但没有带类型实参,例如:

class MyArray<T>{
    public T[] obj = (T[])new Object[3];
    public T getPos(int pos){
        return obj[pos];
    }
    public void setObj(int pos, T val){
        obj[pos]=val;
    }
}
public class Demo1 {
    public static void main(String[] args) {
        MyArray list = new MyArray();
    }
}

但请注意:裸类型是为了兼容老版本的API,在编程时请避免裸类型的使用;

2.5 泛型类的编译

2.5.1 擦除机制

class MyArray<T>{
    public T[] obj = (T[])new Object[3];
    public T getPos(int pos){
        return obj[pos];
    }
    public void setObj(int pos, T val){
        obj[pos]=val;
    }
}
public class Demo1 {
    public static void main(String[] args) {
        MyArray<Integer> myArray = new MyArray<Integer>();
        myArray.setObj(0,85);
        int ret = myArray.getPos(0);
        System.out.println(ret);
    }
}

查看上文代码的字节码文件:

在编译完成后,运行过程中是没有泛型的概念的,此时所有的T都被擦除为Object,通过上述的编译器生成的字节码文件中是不包含泛型的类型信息的;

2.5.2 泛型类型数组的实例化

java语法规定:在实例化数组时必须提供具体的元素类型,故而不能直接实例化泛型类型数组

方法1:(上文擦除机制部分示例代码的在泛型类中创建数组的方法):

class MyArray<E>{
    public E[] obj = (E[]) new Object[3];
}

但该种写法存在问题:试运行以下代码:

class MyArray<E>{
    public E[] obj = (E[]) new Object[3];

    public E getPos(int pos){
        return obj[pos];
    }
    public void setObj(int pos,E val){
        obj[pos]=val;
    }
    // 返回数组的方法
    public E[] getArray(){
        return obj;
    }
}
public class Demo1 {
    public static void main(String[] args) {
        MyArray<Integer> myArray = new MyArray<Integer>();
        myArray.setObj(0,10);
        myArray.setObj(1,85);
        Integer[] integers = myArray.getArray();
    }
}

报错及分析如下:

在继承与多态章节已经提到过,向下转型是不安全的;

java的数组实现非常特殊,

此处可以理解为:jvm认为使用一个固定类型如Integer或String等等类型的对象来接收Object类型对象是不安全的;

正确代码为:(使用反射)

class MyArray<E>{
    public E[] obj;
    public MyArray(Class<E>clazz, int capacity){
        // 参数为数组元素类型与容量
        obj = (E[])Array.newInstance(clazz, capacity);
    }
    public E getPos(int pos){
        return obj[pos];
    }
    public void setObj(int pos, E val){
        obj[pos]=val;
    }
    public E[] getArray(){
        return obj;
    }
}
public class Demo1 {
    public static void main(String[] args) {
        MyArray<Integer> myArray = new MyArray<>(Integer.class,10);
        myArray.setObj(0,1997);
        myArray.setObj(1,85);
        Integer[] integers = myArray.getArray();
    }
}

但以上方法并不常用,更推荐方法2:; 

继承与多态文章链接如下:

https://mp.csdn.net/mp_blog/creation/editor/129814488

方法2:参考java的ArrayList源码的get方法:

对于泛型类型数组的实例化,不能直接使用泛型类型作为数组元素的类型,故而可以使用Object类结合强制类型转换实现泛型类型数组的实例化:

class MyArray<E>{
    public Object[] obj = new Object[3];
    public E getPos(int pos){
        return (E)obj[pos];
    }
    public void setPos(int pos, Object val){
        obj[pos] = val;
    }
}

2.6 泛型的上界

2.6.1  <E extends N> N为接口

表示实例化对象时指定的参数类型一定已经实现了N接口;

// 写一个泛型类求一个数组的最大值
class Alg<E extends Comparable<E>>{
    public E findMax(E[] array){
        E max=array[0];
        for(int i=0;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>();
        Integer[] array = {1,4,2,10,9,8,17,5};
        System.out.println("Array is:");
        for(Integer x:array){
            System.out.print(x+" ");
        }
        System.out.println();
        Integer val =  alg.findMax(array);
        System.out.println("The max element in the array is "+val);
    }
}

输出结果为:

   

 2.6.2  <E extends Number> Number为类

表示E是Number的子类或E就是Number本身;

参考Number (Java Platform SE 8 ) (oracle.com)

 Integer、Double、Long、Short等等都是Number常见的直接子类:

class A<E extends Number>{
    A<Number> a1 = new A<>();
    A<Integer> a2 = new A<>();
    A<Double> a3 = new A<>();
    // A<String> a4 = new A<>();
}

3. 泛型方法

3.1 语法

方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表){...}

3.2 泛型方法示例

3.2.1 普通类的普通泛型方法

class Alg2{
    public<E extends Comparable> E findMax(E[] array){
        E max = array[0];
        for(int i=0;i<array.length;i++){
            if(max.compareTo(array[i])<1){
                max=array[i];
            }
        }
        return max;
    }
}
public class Demo4 {
    public static void main(String[] args) {
        Alg2 alg2 = new Alg2();
        Integer[] arr = {19,9,7,85,25};
        System.out.println("Array is: ");
        for(Integer x:arr){
            System.out.print(x+" ");
        }
        System.out.println();
        Integer val = alg2.<Integer>findMax(arr);
        System.out.println("The max element in the array is "+val);
    }
}

输出结果为:

 注:调用泛型方法时泛型类型可以省略:

 Integer val = alg2.<Integer>findMax(arr);

 上行代码可以简写为:

 Integer val = alg2.findMax(arr);

编译器会根据传递给泛型方法的参数arr的类型判断E的类型;

3.2.2 普通类的静态泛型方法

class Alg2{
    public static<E extends Comparable> E findMax(E[] array){
        E max = array[0];
        for(int i=0;i<array.length;i++){
            if(max.compareTo(array[i])<1){
                max=array[i];
            }
        }
        return max;
    }
}
public class Demo4 {
    public static void main(String[] args) {
        Integer[] arr2 = {19,9,7,85,25};
        System.out.println("Array is: ");
        for(Integer x:arr2){
            System.out.print(x+" ");
        }
        System.out.println();
        Integer val = Alg2.findMax(arr2);
        System.out.println("The max element in the array is "+val);
    }
}

输出结果为:

静态泛型方法不再依赖于类的对象存在,可以使用类名直接调用;

猜你喜欢

转载自blog.csdn.net/m0_63299495/article/details/130754268