Java-泛型

目录

泛型的定义

什么是泛型呢?泛型为什么会出现呢?相信大家都有很多疑问而且也相信很多人只是知道有泛型这个东西,而关于泛型的其他一概不知。泛型可以理解为适用许多不同类型,对于其语法看,就是对类型实现了参数化。

为什么会出现泛型呢?当我们定义一个数组的时候,类型都是固定好的,我们存放内容的时候只能存储我们指定好的类型,这样会比较死板,因为当我们定义一个类或者接口,创建对象后,所传入的数据不一定就是我们指定的数据类型,这样也就会导致出错了,所以为了使数组能够盛放不同的数据类型,所以引入了泛型。

为什么不用object接收呢?

java 复制代码
class MyArray {
    public Object[] array = new Object[10];
    public Object getPos(int pos) {
        return this.array[pos];
    }
    public void setVal(int pos,int val) {
        this.array[pos] = val;
    }
    public void setVal(int pos,String val) {
        this.array[pos] = val;
    }
}
public class Test02 {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.setVal(0,10);
        myArray.setVal(1,"hello");//字符串也可以存放
        String ret = myArray.getPos(1);//编译报错
        System.out.println(ret);
    }
}
  1. 像上面的代码,我们直接用Object进行接收,这满足了可以接收任何数据类型,不是仅仅一种。
  2. 虽然在1位置存入的确实是字符串,但是用Object接收,所以倒数第二行会编译错误,我们需要进行类型强转才可执行。
  3. 通过观察上面代码,我们也可以发现,确实是什么类型的都可以存放,但我们定义的数组的概念是什么呢?不是存储同一种类型的数据嘛,这样虽然满足可以接收任何类型的数据,但是一个数组中存入的类型却五花八门的,混乱得很,所以这也就体现了引入泛型的必要性!泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。

泛型的语法

java 复制代码
class 类名 <类型形参列表> {
//在类中可以使用类型参数
}
//例子
class MyArray<T> {
public Object[] = new Object[10];//1
public T getPos(int pos) {
return this.array[pos];
}
public void setVal(int pos,T val) {
this.array[pos] = val;
}
}
public class TestDemo {
public static void main(String[] args) {
MyArray<Integer> myArray = new MyArray<>();//2
myArray.setVal(0,10);
myArray.setVal(1,12);
int ret = myArray.getPos(1);//3
System.out.println(ret);
myArray.setVal(2,"bit");//4
}
}
  1. 类名后面的<类型形参>表示占位符,表示当前类是一个泛型类。

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

E 表示 Element

K 表示 Key

V 表示 Value

N 表示 Number

T 表示 Type

S, U, V 等等 - 第二、第三、第四个类型

  1. 并不是不可以用object数组来接收,要和泛型一起搭配使用,效果最佳。
  2. 注释1处不可以new泛型类型数组 T[] = new T[];这种是错误的。一会后面讲解。public T[] array = (T[])new Object[10];这样定义数组的方法虽然这也能运行,但是不建议这样写,因为不知道啥时候就会出错。
  3. 注释4处,代码编译报错,此时因为在注释2处指定类当前的类型,此时在注释4处,编译器会在存放元素的时候帮助我们进行类型检查。

泛型类的使用

java 复制代码
泛型类<类型实参> 变量名; // 定义一个泛型类引用
new 泛型类<类型实参>(构造方法实参); // 实例化一个泛型类对象
//例
Array<Integer> array = new Array<Integer>();

这里要注意!
对于类型实参,也就是泛型,它只接受类!我们不允许传入基本数据类型,如果想传基本数据类型的时候,我们要传入对应的包装类

  1. 当编译器可以根据上下文推导出类型实参时,可以省略类型实参的填写。例:MyArray list = new MyArray<>();

泛型的编译

在编译过程中,所有的T都会编程Object,这个过程我们也可以称为擦除机制。Java的泛型机制只存在于编译期间,在运行的时候,JVM根本不认识泛型T,所以运行期间根本不包含泛型的类型信息。

这也就是为什么我们不可以创建泛型对象的原因。

泛型的上界

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

java 复制代码
class 泛型类名称<类型形参 extends 类型边界> {
...
}
//例子
public class MyArray<E extends Number> {
...
}
//这个的意思是只可以接收Number的子类或者本身作为实参
  1. 所有包装类都是Number的子类。
  2. 当我们没有设置边界的时候,就表示可以接受Object的子类或者本身。
  3. 有个比较复杂实现
java 复制代码
public class MyArray<E extends Comparable<E>> {
...
}
//这个我们要求接收的必须是实现了Comparable的类

泛型方法

java 复制代码
方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }
//例:
public class Util {
public static <E> void swap(E[] array, int i, int j) {
E t = array[i];
array[i] = array[j];
array[j] = t;
}
}

普通使用方法:

java 复制代码
String[] b = { ... };
Util.<String>swap(b, 0, 9);

使用了类型推导后的使用方法:(类型推导:根据实参传值来推导当前类型)

java 复制代码
Integer[] a = { ... };
swap(a, 0, 9);
String[] b = { ... };
swap(b, 0, 9);

包装类

为什么要有包装类呢?包装类的定义是什么呢?

包装类(Wrapper Class): Java是一个面向对象的编程语言,但是Java中的八种基本数据类型却是不面向对象的,为了使用方便和解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八种基本数据类型对应的类统称为包装类(Wrapper Class),包装类均位于java.lang包。

装箱和拆箱

  1. 装箱操作,将int类型的数据放入到Integer对象的某个属性中。
java 复制代码
int i = 9;
Integer ii = Integer.valueOf(i);
Integer il = new Integer(i);
//对象中哪个属性用到,就把基本数据类型的值传过去。
  1. 拆箱操作,将Integer对象中的值取出放到一个基本数据类型中。
java 复制代码
int i = 9;
Integer il = new Integer();
int j = il.intValue();

自动装箱拆箱

java 复制代码
int i = 10;
Integer ii = i; // 自动装箱
Integer ij = (Integer)i; // 自动装箱
int j = ii; // 自动拆箱
int k = (int)ii; // 自动拆箱

下面大家来看看这个题:

java 复制代码
public static void main(String[] args) {
    Integer a = 127;
    Integer b = 127;
    Integer c = 128;
    Integer d = 128;
    System.out.println(a == b);
    System.out.println(c == d);
}

最后输出为一个true,一个false

在Integer对象创建过程中:如果传入value数值在-128~127范围内,那么所有在这个范围内被创建的对象实际都指向同一个地址,即被预创建Integer对象所在的地址。

如果传入value数值不在范围内,那么每次被创建的对象(句柄)都指向一个新的且不同的地址,即通过new关键字由JVM分配的新地址。

相关推荐
西岭千秋雪_几秒前
谷粒商城のElasticsearch
java·大数据·服务器·spring boot·elasticsearch·搜索引擎
QXH20000013 分钟前
数据结构—双向链表
c语言·数据结构·算法·链表
bluebonnet2713 分钟前
【Rust练习】15.match 和 if let
开发语言·后端·rust
yueqingll28 分钟前
020、二级Java选择题综合知识点(持续更新版)
java·开发语言
旺小仔.32 分钟前
【数据结构篇】~排序(1)之插入排序
c语言·数据结构·算法·链表·性能优化·排序算法
计算机学姐1 小时前
基于python+django+vue的在线学习资源推送系统
开发语言·vue.js·python·学习·django·pip·web3.py
A洛1 小时前
Cloudflare Pages 部署 Next.js 应用教程
开发语言·github·nextjs·cloudflare·cloudflarepages
lly2024061 小时前
Rust 函数
开发语言
许野平1 小时前
Rust:深入浅出说一说 Error 类型
java·开发语言·rust·error
jingling5551 小时前
后端开发刷题 | 数字字符串转化成IP地址
java·开发语言·javascript·算法