泛型和包装类
一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。而泛型出现的作用就是使其具备多种类型(类型进行参数化)
例如:若你的方法参数是一个泛型的数组,那么你在调用方法的时候,不管你传递什么类型的参数它都可以进行接收
引入泛型
实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值?
思路:
- 我们以前学过的数组,只能存放指定类型的元素
- 我们学过继承后,就知道所有的类的父类都是 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);
}
}
上述代码存在的问题:
- 任何类型都可以存放,导致在取元素的时候,必须进行强制类型转换(这就大大降低了程序员的编码效率)
虽然在这种情况下,当前数组任何数据都可以存放,但是,更多情况下,我们还是希望他只能够持有一种数据类
型。而不是同时持有这么多类型。所以,泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译
**器去做检查。**此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。
泛型语法
- ,就指定了当前数组中只能存放Integer类型的数据
- 泛型在编译期间帮我们做了两件事
- 存元素的时候进行类型检查
- 取元素的时候,进行类型强转
- 泛型的 <> 中只能放 引用类型,不能放置基本类型(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);
}
}
解释:
-
类名后的 代表占位符,表示当前类是一个泛型类
-
E 表示 Element
-
K 表示 Key
-
V 表示 Value
-
T 表示 Type
-
// 解释:若能实例化成功的话,就表示T[]能够作为方法参数进行返回,而T[]中能存放任何类型的数据;这个时候外部进行接收此方法返回值的时候,就无法进行接收(强转也不行) 这也属于Java的一个安全机制吧
public T[] array = new T[5]; // 这个为啥会报错?
擦除机制
泛型在编译的时候,其中的泛型都会进行擦除为 Object
有关泛型擦出机制的文章介: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());
}
}
上界
// 一般用来读取元素
// 这里可以接收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)