【RE:布丁JAVA学习】这大概是史上最详细的JAVA泛型教程,不看后悔。

引言

【RE:布丁JAVA】是一个java小白布丁萨玛浑浑噩噩工作三年之后发现自己java基础不行而重新学习java的系列,喜欢的同学可以点个<font color=red >关注</font>,如果有什么问题可以在评论区发表<font color=red >评论</font>,谢谢🙏

愿:<font color=red >天下程序猿头发乌黑亮丽</font>

概述

在我刚开始学习JAVA的时候经常看到<T> <K> <T,K> <? extends Number> 等等类似的写法,当时就很疑惑,这些到底是什么为什么有的时候是'T'有的时候是'K'而有的时候是'?',是有什么规则么?这些代表的又是什么呢?我想要使用<J>可以吗?
下面就要我们带着这种种疑问来看下我们今天的主题==泛型'==。

1、为什么要使用泛型

我们学习泛型,首先要明白==我们为什么要使用泛型==

举个有点不优雅例子

小A过生日举行了一个生意派对,于是他准备了一个箱子。告诉大家没人可以把放进去一个食物。到时候自己会把他们一个一个的吃掉。于是小B放了苹果、小C放了橘子。
但是,我放了一坨屎进去。
然后小A在生日派对上就吃了shit发生了不可以思议的事情。
于是小A之后再办这种事情的时候,会让自己的管家检查放进入的是不是食物。不是食物就是不让放进去。这样他就不会吃屎了。
而这个管家就可以认为是泛型。为了约束放入箱子东西的类型。

好了好了,看完上面这个例子同学们一定都知道了什么是泛型了,为什么使用泛型(为了防止吃shit )下面我们就开认真讲一下。

2、什么是泛型

同学们都知道在我们开发的时候错误可以分为两种,

编译时错误

在编译阶段由java编译器发现的错误。

编译时错误

如上图所示,编译器发现我们的代码错误,会提示我们,而我们开发人员必须要修改错误之后,才可以通过编译。这就是编译时错误

运行时错误

编译时未报错,在运行时抛出异常。


在这里插入图片描述

如上图所示在编译的时候,是不会曝出错误。但是运行的时候就会抛出==ClassCastException==的错误,这样就称之为运行时错误。


在这里插入图片描述

从JDK5开始所有的Java集合都采用了泛型的机制。在声明集合变量的时候,可以使用‘<>’指定集合中的元素类型。

例子如下图


在这里插入图片描述

所以说:

==泛型就是为了把 ClassCastException 运行时错误转换成编译时错,是为了约束参数类型而诞生的。==

泛型的主要用于==泛型类==、==泛型接口==、==泛型方法==、==泛型数组==

下面就让我们一次来介绍以下。

3、泛型类&泛型接口

首先我们先来看一个正常的类,然后我们尝试把他改造成一个泛型类。


public class Printer {
    /**
     * 打印文字
     */
    Object printText;

    public Printer(Object printText) {
        this.printText = printText;
    }

    public Object getPrintText() {
        return printText;
    }

    public void setPrintText(Object printText) {
        this.printText = printText;
    }
    
    public static void main(String[] args) {
     Printer printer = new Printer("测试打印文本");
     // 运行时错误 抛出ClassCastException
     Integer o = (Integer) printer.getPrintText();
    }
}

在main方法中代码调用printer类,获取printText进行了强制转换编译可以通过但是运行就会报错,抛出ClassCastException错误。
为了防止这种情况出现,我们就可以对printer类进行改造。

public class Printer<T> {
    /**
     * 打印文字
     */
    T printText;

    public Printer(T printText) {
        this.printText = printText;
    }

    public T getPrintText() {
        return printText;
    }

    public void setPrintText(T printText) {
        this.printText = printText;
    }

    public static void main(String[] args) {
        Printer<String> printer=new Printer<String>("这是测试打印文本");
        Integer o = (Integer) printer.getPrintText();// 编译错误
        String a = printer.getPrintText();// 合法
    }
}

就像在定义方法的时候可以声明一些方法参数,在定义类的时候我们可以通过< T >的形式类声明类型参数,在类主题中可以直接引用 T。

这种带有类型参数的类就被称为泛型类。

上面的printer类就是一个泛型类,他有一个类型参数T,而在main方法中初始化printer类的时候指定的T为String,所以在之后的get方法中编译器会知道返回值为String,所以使用Integer接收的时候会编译错误。而使用String接收的时候就是合法的。

泛型类格式:

修饰词 class 类名 <T> {...}

如:

public class printer <T> {
T content;
}

public class people <J> {
J name;
}

这里的T只是习惯叫法而已。如果你乐意也可以用其他字母代替 比如 B、Y、H。都是可以的。

一个泛型类也可以有多个类型参数,多个类型参数放在一个<>中使用‘,’隔开,例子如下:


public class Printer<T,K> {
    
    Map<T,K> map;

    public Map<T, K> getMap() {
        return map;
    }

    public void setMap(Map<T, K> map) {
        this.map = map;
    }

    public static void main(String[] args) {
        Printer<String,Integer> printer=new Printer<String, Integer>();
        printer.getMap().put("key",1);// 合法
        printer.getMap().put(1,"key");// 编译错误
    }
}

泛型接口与泛型类的用法基本一致

泛型接口的格式

修饰词 interface 接口名称 <T> {...}

这里我们就举一个例子看下:

public interface people <T> {
    T setName(T name);
}

4、泛型方法

在一个方法中,如果方法的参数或者返回值中带有<font color=red>< T ></font>形式的类型参数,那么这个方法称为泛型方法。在普通的类或者泛型类中都可以创建泛型方法。

泛型方法结构

// 返回值 、参数 、 方法体 都可以引用 K类型参数
修饰词 <K> 返回值 方法名称(参数) {方法体}

例子如下


public class Printer<T> {

    T printText;
    
    // 不是泛型方 T只是引用了类的类型变量而已。并没有自己定义
    public T getPrintText() {
        return printText;
    }
    // 是泛型方法 自己定义了类型变量K
    public <K> K print(K text) {
        System.out.println("打印文本:" + text);
        return text;
    }

    public static void main(String[] args) {

        Printer printer=new Printer();
        printer.print("测试打印文本");
    }
}

例子中的getPrintText()方法虽然有T但是并不是泛型方 T只是引用了类的类型变量而已。并没有自己定义。
而print()方法是泛型方法因为自己定义了类型变量K。

5、泛型数组

泛型数组是指数组的类型是‘T’的数组,如‘T[]’;

例子

public class Printer<T> {
    // 泛型数组
    public T[] content;

    public T[] getContent() {
        return content;
    }

    public void setContent(T[] content) {
        this.content = content;
    }

    public static void main(String[] args) {
        Printer<String> printer = new Printer<String>();
        printer.setContent(new String[]{"元素1", "元素2"});
        System.out.println(printer.getContent()[0]);
    }
}

==注意==:在泛型类中不能用泛型数组来创建数组实例。

public class Printer<T> {
    // 泛型数组
    public T[] content = new T[10]; //编译出错,不能用泛型数组来创建数组实例。
}

5、extends关键词

在定义泛型的时候,我们可以使用extends关键字来限定类型参数,语法格式为

<T extends 类名>/<T extends 接口名>
比如我们刚开始的例子中,我们想要放入箱子的食物都是甜品,那么我们就可以写 <T extends 甜品>

例子:

public class Printer<T extends Number> {

    public T content;

    public T getContent() {
        return content;
    }

    public void setContent(T content) {
        this.content = content;
    }

    public static void main(String[] args) {
        Printer<Integer> printer = new Printer<Integer>(); // 合法;Integer是Number子类
        Printer<Double> printer1 = new Printer<Double>();  // 合法; Double是Number子类
        Printer<String> printer2 = new Printer<String>();  // 编译错误;String不是Number子类
    }
}

在上面的例子中,我们在print类中定义了一个 <T extends Number>,代表引入的参数类型必须是Number的子类。
根据下图我们知道 Integer 和 Double都是Number的子类,所以合法,而String并不是Number的子类,所以会出现编译错误。

在这里插入图片描述

6、"?" 通配符

‘?’是表示一个不确定的类型。由于不是像‘T’一样是一个确定的类型,所以‘?’无法用于定义变量或者类。一般用于集合中。

‘?’可以使用有上限和下限对类型进行约束

‘?’的上限‘<? extends 类名/接口>’

List<? extends Number> list = new ArrayList<>();

‘?’的下限‘<? super 类名/接口>’

List<? super Number> list = new ArrayList<>();

关于通配符还有很多东西可以讲,比如List<? extends Number> 无法add除了null只为的元素,这个我之后准备专门开一篇讲解,如果有想要知道的同学可以点个关注。

7、注意事项

1、泛型只在编译期间有效,在编译之后的字节码文件中就会被清除。
所以以下两个list对象是相等的

List<Srting> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();

System.out.println(list1.getClass()==list2.getClass()) // 打印true

因为泛型在编译之后,所以编译器也是不允许在一个类定义两个同名的方法,参数分别是List<T>List<K>

public class Printer<T,K> {
   public void print(List<T> list){
       
   }
   // 编译错误,非法的方法重载
   public void print(List<K> list2){
       
   }
}

2、不可以对泛型进行强制转换,这样存在安全隐患,会导致抛出ClassCastException异常。

        Collection list1 = new ArrayList<Integer>();
        list1.add(1);
        List<String> list2 = new ArrayList<String>();
        list2 = (ArrayList<String>) list1;
        for (String s:list2){
            System.out.println(s); // 抛出异常ClassCastException
        }

3、不能对泛型进行instanceof操作。

   public void print(List<K> list){
       // 编译错误
       if(list instanceof Collection<String>){
           
       }
   }

8、参考

8、结语

本片文章主要讲了泛型的使用,我们知道了泛型主要是为了是编译器在编译的时候能够判断我们的参数是否正确,从而避免运行时错误。并且可以简化代码编写,无需进行多余的强制转换。
然后我们学习了如何定义 泛型类、泛型方法、泛型接口 等。
如果文章有什么错误后者同学们有疑问的地方,欢迎评论留言,
如果您喜欢,欢迎<font color=red>关注、点赞</font> 谢谢🙏

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352