java泛型初阶和包装类

文章目录

    • 1 包装类
    • 6 泛型如何编译的
    • 6.1 擦除机制
      • 6.2 为什么不能实例化泛型类型数组
    • 7 泛型的上界
      • 7.1 语法
      • 7.2 示例
      • 7.3 复杂示例
    • 8 泛型方法
      • 8.1 定义语法
      • 8.2 示例
      • 8.3 使用示例-可以类型推导
      • 8.4 使用示例-不使用类型推导

1 包装类

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

6 泛型如何编译的

6.1 擦除机制

那么,泛型到底是怎么编译的?这个问题,也是曾经的一个面试问题。泛型本质是一个非常难的语法,要理解好他
还是需要一定的时间打磨。

通过命令:javap -c 查看字节码文件,所有的T都是Object。
java泛型初阶和包装类_第1张图片

在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制。

Java的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息。

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

提出问题:

1、那为什么,T[] ts = new T[5]; 是不对的,编译的时候,替换为Object,不是相当于:Object[] ts = new
Object[5]吗?

2、类型擦除,一定是把T变成Object吗?

6.2 为什么不能实例化泛型类型数组

小结: 基类数组不能直接强制类型转换赋值给子类数组,因为基类数组可能存在其他类型的子类

讲解步骤:

  • 抛出问题代码
  • 分析代码错误原因
  • 具体解释错误原因
  • 解决问题
  • 开发中正确写法

代码1:

class MyArray<T> {
	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 T[] getArray() {
		return array;
	}
}
public static void main(String[] args) {
		MyArray<Integer> myArray1 = new MyArray<>();
			Integer[] strings = myArray1.getArray();//erro
}	
/*
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
at TestDemo.main(TestDemo.java:31)
*/

原因:擦除替换后, 将Object[]分配给Integer[]引用,程序报错。

// 替换前
public T[] getArray() {
	return array;
}
// 替换后
public Object[] getArray() {
	return array;
}

通俗讲就是:返回的Object数组里面,可能存放的是任何的数据类型,可能是String,可能是Person,运行的时
候,直接转给Integer类型的数组,编译器认为是不安全的。

如下两点详细解释

1.如下代码,在java中,new必须指定确定类型

class MyArray<T> {
    //public T[] array = (T[])new Object[10];
    public T[] array = new T[2];
    public T getPos(int pos) {
        return this.array[pos];
    }
    public void setVal(int pos,T val) {
        this.array[pos] = val;
    }
    public T[] getArray() {
        return array;
    }
}

2.在Java中,不能将Object类型的数组分配给子类的数组,因为这会违反Java的类型安全性规则。如果可以这样做,那么子类数组可能包含其它类型的元素,从而导致类型转换异常或者其他运行时错误。要将一个Object类型的数组分配给子类的数组,需要使用强制类型转换并确保所有元素都是正确的子类类型。
例如:

ojbecj对象是所有类的基类,下面是基类与子类的例子,假设有一个父类Animal和两个子类Cat和Dog,如果想要将一个Object类型的Animal数组分配给一个Cat类型的数组,需要进行以下强制类型转换:

    public static void main(String[] args) {
 // erro1:
      Animal[] animalArray = new Animal[2];
      Cat[] catArray = (Cat[]) animalArray;//erro
    }
     // 报错:
		//Exception in thread "main" java.lang.ClassCastException: Dog cannot be cast to Cat
		//at genericArray.main(genericArray.java:68)

但是这样做会导致运行时错误,因为animalArray实际上并不是一个Cat数组,其中可能包含其他类型的Animal对象。

正确的做法是,使用for循环逐一将Object类型的元素转换为Cat类型,并添加到Cat类型的新数组中,例如:

    public static void main(String[] args) {
//  success:
        Animal[] animalArray = new Animal[2];
        Cat[] catArray = new Cat[2];
        for (int i = 0; i < animalArray.length; i++) {
            catArray[i] = (Cat) animalArray[i]; // 进行强制类型转换
        }
    }

这样可以确保每个元素都被正确地转换为Cat类型。但是需要注意,如果animalArray中包含了除Animal以外的其它类型的对象,则会在转换时抛出ClassCastException异常。

    public static void main(String[] args) {
//  erro2:        
       Animal[] animalArray = new Animal[2];
       animalArray[1] = new Dog(); // Animal[]数组包含了其他Animal子类
    	Cat[] catArray = new Cat[2];
       for (int i = 0; i < animalArray.length; i++) {
           catArray[i] = (Cat) animalArray[i]; // 进行强制类型转换
           //erro i==1时,Dog错误类型转换
      }
 // 报错:
//Exception in thread "main" java.lang.ClassCastException: Dog cannot be cast to Cat
	//at genericArray.main(genericArray.java:68)

解决办法: 【了解即可,开发中不会用到】

  • 有点神奇!通过反射创建,指定类型的数组
class MyArray1<T> {
    public T[] array;

    public MyArray1() {
    }
    public MyArray1(Class<T> clazz, int capacity) {
        array = (T[]) Array.newInstance(clazz, capacity);
    }
    public T getPos(int pos) {
        return this.array[pos];
    }
    public void setVal(int pos,T val) {
        this.array[pos] = val;
    }
    public T[] getArray() {
        return array;
    }
}

实际开发是这样的:

class MyArray3<T> {
    //public T[] array = (T[])new Object[10];
    public Object[] array = new Object[2];
    public T getPos(int pos) {
        return (T)array[pos];
    }
    public void setVal(int pos,T val) {
        this.array[pos] = val;
    }
    public Object[] getArray() {
        return array;
    }
}
public class genericArray {
    public static void main(String[] args) {
        MyArray<Integer> myArray = new MyArray<>();
        myArray.setVal(0,1);
        myArray.setVal(1,0);
        Object a = myArray.getPos(0);
        System.out.println(a);
        // 不用转换类型也可以的,因为返回的就是object
        Object b = (Object)myArray.getPos(1);
        System.out.println(b);
        //Integer[] int_array = myArray.getArray(); erro 基类数组不能给子类
        // 获取数组后,访问数组元素需要强制转换.
        Object[] int_array = myArray.getArray();
        System.out.println((Integer)int_array[0]);
    }
}

7 泛型的上界

在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。

7.1 语法

class 泛型类名<泛型新参 extend 类型边界>{

}

具体意思通过例子说明。

7.2 示例

public class MyArray<E extends Number> {
........

}

接受 Number 的子类型作为 E 的类型实参

MyArray<Integer> l1; // 正常,因为 Integer 是 Number 的子类型
MyArray<String> l2; // 编译错误,因为 String 不是 Number 的子类型
error: type argument String is not within bounds of type-variable E
MyArrayList<String> l2;
^
where E is a type-variable:
E extends Number declared in class MyArrayList

了解: 没有指定类型边界 E,可以视为 E extends Object

7.3 复杂示例


// #2 语法规定只允许有使用Comparable接口的类型
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 < array[i]) #1 erro : 引用类型无法比较
            // 只能实现接口compareTO()
            if(max.compareTo(array[i])<0)
            {
                max = array[i];
            }
        }
        return max;
    }
}
public class Main {
    public static void main(String[] args) {
        Alg<Integer> alg = new Alg();
        Integer[] array = {1,2,3,4,5};
        // 根据array自动类型推导
        System.out.println(alg.findMax(array));
        // 新语法 <类型> 指定类型
        System.out.println(alg.<Integer>findMax(array));
        // Alg alg2 = new Alg(); // erro
        //#3
        // Alg alg2 = new Alg(); // erro
    }
}

8 泛型方法

8.1 定义语法

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

8.2 示例

静态的泛型方法 需要在static后用<>声明泛型类型参数

class Alg1{

    public static<T extends Comparable<T>> T findMax(T[] array){
        T max = array[0];
        for(int i=1;i<array.length;i++){
            // if( max < array[i]) #1 erro : 引用类型无法比较
            // 只能实现接口compareTO()
            if(max.compareTo(array[i])<0)
            {
                max = array[i];
            }
        }
        return max;
    }
}

 public class Main {
     public static void main(String[] args) {
         Integer[] array = {1,2,3,4,5};

         // 根据array自动类型推导
         System.out.println(Alg1.findMax(array));
         // 新语法 <类型> 指定类型
         System.out.println(Alg1.<Integer>findMax(array));
     }
  }

8.3 使用示例-可以类型推导

 public class Main {
     public static void main(String[] args) {
         Integer[] array = {1,2,3,4,5};

         // 根据array自动类型推导
         System.out.println(Alg1.findMax(array));

     }
  }

8.4 使用示例-不使用类型推导

 public class Main {
     public static void main(String[] args) {
         Integer[] array = {1,2,3,4,5};


         // 新语法 <类型> 指定类型
         System.out.println(Alg1.<Integer>findMax(array));
     }
  }

你可能感兴趣的:(JAVA,java,jvm,开发语言)