🌱前置知识——泛型
在正式学习数据结构之前,我们先要了解泛型的相关知识~
泛型是一个什么东东?它有什么用处呢?🧐
首先我们想创建一个类,这个类可以让我们放任意类型的元素,并且能够得到或者获取该元素,在没有学泛型之前,我们是十分难以做到的。若要以未学习泛型之前的知识做出一个可以接收任意元素的话,我们需要用到Object,代码如下👇:
class MyArray{
public Object[] array = new Object[10];
public void setPos(Object str, int index){
this.array[index] = str;
}
public Object getPos(int index){
return array[index];
}
}
public class demo2 {
public static void main(String[] args) {
MyArray myArray = new MyArray();
myArray.setPos(1,0);
myArray.setPos("Haiihaihai",1);
myArray.setPos('a',2);
int a = (int)myArray.getPos(0);
System.out.println(a);
String b = (String)myArray.getPos(1);
System.out.println(b);
char c = (char)myArray.getPos(2);
System.out.println(c);
}
}
虽然能够实现,但是我们在这里的使用是有局限性的:每次获取类型我们都需要强制类型转换,并且我们不知道获取的元素是什么类型。
在这时候我们就需要泛型,来指定固定类型。
🌿泛型
占位符 <T> :代表当前类是一个泛型类。
在加入的上面的占位符后就算是使用泛型了,让我们看看是怎么样使用的吧~👇
class MyArray<T>{
public Object[] array = new Object[10];
public void setPos(T str, int index){
array[index] = str;
}
public T getPos(int index){
return (T)array[index];
}
}
public class demo3 {
public static void main(String[] args) {
MyArray<Integer> myArray1 = new MyArray<>();//在后面的<>可以省略里面的类型,但是前面的<>不可以省略
myArray1.setPos(1,0);
myArray1.setPos(2,1);
int str1 = myArray1.getPos(0);
int str2 = myArray1.getPos(1);
System.out.println(str1);
System.out.println(str2);
}
}
由输出结果我们可以知道,泛型确实有规定指定类型的作用,若不放入指定类型,会得到下面的结果👇:
🍃泛型的作用
综上,我们可以将泛型的作用总结为下面两点:
- 在放入元素的时候指定类型
- 在给出元素的时候转换类型,也就是编译的时候自动进行类型检查和转换
Tips:在 <> 里面的类型一定要是类类型的,如Integer、Character、String等
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
🌿java的擦除机制
java中的泛型在编译的时候编译器会将其变成Object类型:
但是如果要我们来实现T[ ]数组的话,其实形式会有点奇怪,如下👇
public T[] array = (T[])new object[10];
这时候我们会疑惑🤯为什么在写泛型数组的时候不能直接用平常创建数组的样子来写代码就像这样👇
//这种写法是错误的!!!!
public T[] array = new T[10];
这都是由于java中的擦除机制所导致的,java中的数组很特殊。
我们已知由于擦除机制 T 会在编译的时候被换成Object类型,就变成public Object[ ] array = new Object[10]; 我们会觉得这不是和之前的直接用Object类型创建的数组是一样的么?
确实是一样的,但是我们要注意在这之后,当我们要使用这个数组的时候,会出现错误👇
所以就算 T[ ] 按照上面的形式创建数组,后面的操作也是行不通的,所以这样的形式来创建T[ ]数组没有意义,于是在java中就不可以有public T[ ] array = new T[10]; 这样子的形式了,会直接报错。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
🌿泛型的上界
泛型的上界作用是用于限制一定范围的类型来创建变量,其形式如下👇
class Symbol<类型形参 extends 类型边界>{
}
class Numbers{
}
class Integers{
}
class Symbol<T extends Numbers>{
//在这里只接受Numbers类型以及Numbers类型的子类
}
✨泛型上界的复杂用法①:
当我们使用泛型类的时候,如果要对里面的元素进行比较,由于java的擦除机制我们可以知道,里面的类似于占位符T的类型都会被替换成Object类型,而Object类型里面没有Comparable接口的,无法做到使用compareTo方法来比较。
所以,如果要比较泛型类里面的元素大小的话,我们要先继承Comparable接口:
class Flg<T extends Comparable<T>>{
public T findMax(T[] array){
T max = array[0];
for(int i = 0; i<=array.length; i++){
if(max.compareTo(array[i]) < 0){
max = array[i];
}
}
return max;
}
}
所以在我们创建这个泛型类的时候,只要是继承了Comparable接口的类型,就可以作为上界,然后创建这个变量。
实际使用👇
✨泛型上界的复杂用法②:
又或者我们懒于继承Comparable接口,在这时候就可以使用静态方法👇
class Flg2{
public static<T extends Comparable<T>> T findMax(T[] array){
T max = array[0];
for (int i = 0; i < array.length; i++) {
if(max.compareTo(array[i]) < 0){
max = array[i];
}
}
return max;
}
}
要注意的点是要在static后提前声明
🌿通配符
通配符是用于解决泛型无法协变的问题的
👉人话翻译:用于接收任何泛型类型并读取使用(仅限于读取使用)
👉代码翻译:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
🍃通配符的使用
通配符的使用和占位符的使用方法一样,就是将占位符的符号替换成 ?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
🍃通配符上界与下界
由于通配符只能接收元素与输出元素,并不能对增加元素或者改变该元素,所以在通配符的基础上增加通配符上界与下界的概念
✨通配符上界
通配符上界只可以读取或获取元素,而不能放入元素。其语法如下👇
<? extends 上界>
<? extends Animals> //在这里代表可以接收Animals类以及其子类
✨通配符下界
通配符下界的作用是用于改变其元素,但是不能接收元素,可以将原本元素改变。其语法如下👇
<? super 下界>
<? super Integer> //在这里可以将元素修改为Integer类本身或者其父类
以上!便是全部的啦😎
又是收获满满的一天~