作者:敲代码の流川枫

博客主页:流川枫的博客

专栏:和我一起学java

语录:Stay hungry stay foolish

工欲善其事必先利其器,给大家介绍一款超牛的斩获大厂offer利器——牛客网

点击免费注册和我一起刷题吧

文章目录

1. 泛型概念

泛型的引出

2. 泛型语法

3. 泛型的使用

类型推导(Type Inference)

裸类型(Raw Type)

4. 泛型的擦除机制

5. 泛型的上界

6. 泛型方法

7. 通配符

通配符上界

通配符下界


1. 泛型概念

泛型:就是适用于许多许多类型。从代码上讲,就是对类型实现了参数化

一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。 ——《Java编程思想》

泛型的引出

实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值

可以将数组定义为Object类,因为所有类默认继承于这个类

public class Test {    public static void main(String[] args) {        MyArray myArray = new MyArray();        myArray.setVal(0,10);        myArray.setVal(1, "s");//字符串也可以存放        myArray.setVal(2,10.3);        //String ret = myArray.getPos(1);//编译报错        String ret = (String) myArray.getPos(1);        System.out.println(ret);    }}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;    }}

        //String ret = myArray.getPos(1);//编译报错        String ret = (String) myArray.getPos(1);        System.out.println(ret);

这里我们看到发生了向下转型,需要手动强制类型转换才可以,这样数据很多的时候就会很麻烦

使用Object数组的缺点有二,存放元素时可以存放任何类型的元素,再者,取出元素的时候需要手动强转

此时泛型就解决了这些问题,它将类型参数化了

2. 泛型语法

class 泛型类名称 {// 这里可以使用类型参数}class ClassName {}class 泛型类名称 extends 继承类/* 这里可以使用类型参数 */ {// 这里可以使用类型参数}class ClassName extends ParentClass {// 可以只使用部分类型参数}

注意:

1. 类名后的 代表占位符,表示当前类是一个泛型类

类型形参一般使用一个大写字母表示,常用的名称有:
E 表示 Element
K 表示 Key
V 表示 Value
N 表示 Number
T 表示 Type
S, U, V 等等 – 第二、第三、第四个类型

2. 不能new 泛型类型的数组

T[] ts = new T[5];//是不对的

3.里必须是类类型,不能是简单类型

我们将上述代码改写:

class MyArray {    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 Test {    public static void main(String[] args) {        MyArray myArray = new MyArray();        myArray.setVal(0,10);        myArray.setVal(1, "s");//报错        myArray.setVal(2,10.3);//报错        int a = myArray.getPos(0);//不用强转        System.out.println(a);        MyArray myArray1 = new MyArray();        myArray1.setVal(0,"hello");        myArray1.setVal(1,"world");        String b = myArray1.getPos(0);//不用强转        System.out.println(b);    }}

泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型作为参数传递。需要什么类型,就传入什么类型

泛型存在的意义:

1.在存放类型的时候会进行类型的检查

2.取出元素的时候会自动强制类型转换

注意:泛型时在编译的时候的一种机制,在运行时是没有泛型的概念的

3. 泛型的使用

泛型类 变量名; // 定义一个泛型类引用
new 泛型类(构造方法实参); // 实例化一个泛型类对象

MyArray list = new MyArray();

类型推导(Type Inference)

public class Test {    public static void main(String[] args) {        MyArray myArray = new MyArray();        myArray.setVal(0,10);        int a = myArray.getPos(0);//不用强转        System.out.println(a);        MyArray myArray1 = new MyArray();        myArray1.setVal(0,"hello");        myArray1.setVal(1,"world");        String b = myArray1.getPos(0);//不用强转        System.out.println(b);    }}

编译器可以根据上下文推导出类型实参时,可以省略类型实参的填写

    MyArray myArray = new MyArray();    MyArray myArray1 = new MyArray();

裸类型(Raw Type)

裸类型是一个泛型类型,但是没有实参,取值是还是要强转

注意: 我们不要自己去使用裸类型,裸类型是为了兼容老版本的 API 保留的机制

4. 泛型的擦除机制

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

getPos中返回值为T,被替换为Object,setVal中返回值为空,即V,val参数类型被替换为Object

先根据你制定了类型进行检查和转换,在编译的时候把T都擦除成了Object

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

5. 泛型的上界

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

语法

class 泛型类名称 {        //...}

示例

public class MyArray {        //...}

只接受 Number 的子类型作为 E 的类型,实参没有指定类型边界 E,可以视为 E extends Object

public class Test {    public static void main(String[] args) {        MyArray myArray = new MyArray();        MyArray myArray1 = new MyArray();        MyArray myArray2 = new MyArray();    }}class MyArray {}

6. 泛型方法

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

class Util {    public static  void swap(E[] array, int i, int j) {        E t = array[i];        array[i] = array[j];        array[j] = t;    }}

是泛型的形参,void是返回值,可以直接通过类名.来调用,不用new

非静态

7. 通配符

看一个例子:

class Message {    private T message ;    public T getMessage() {return message;}    public void setMessage(T message) {this.message = message;}}public class TestDemo {    public static void main(String[] args) {    Message message = new Message() ;    message.setMessage("hello");    fun(message);    Message message1 = new Message() ;    message1.setMessage(10);    fun(message1);//报错    }    public static void fun(Message temp){        System.out.println(temp.getMessage());    }}

报错原因是fun方法只能接收Message类型的参数,而传入的是Message类型的参数

接下来我们体会通配符的作用

 public static void fun(Message

通配符的下界,不能进行读取数据,只能写入数据

因为无法确定是哪个父类

说明可以传入的实参的类型必须是Fruit或者Fruit的父类类型

“ 本期的分享就到这里了,记得给博主一个三连哈,你的支持是我创作的最大动力!

ced485cbb11e458d81a746890b32cf3f.gif