Java 泛型详解与范例

目录
  • 一、泛型的使用
  • 二、泛型类的定义-类型边界
  • 三、类型擦除
  • 四、泛型类的使用-通配符
  • 五、泛型方法
  • 六、泛型的限制

一、泛型的使用

前面我们学集合的时候,简单的说过泛型的使用。如下:

ArrayList<Integer> list = new ArrayList<>();
Queue<Integer> queue = new LinkedList<>();

那么使用是这样的简单,该注意什么?

  • 尖括号里的类型,只能写引用类型
  • 基础数据类型的话,就需要写相应的包装类型
  • 泛型只是编译时期的一种机制,在运行时是没有泛型的概念的。

二、泛型类的定义-类型边界

泛型还有一个点就是:泛型的上界。(类型形参 extends 类型边界)有如下代码:

public class Algorithm<T extends Comparable<T>> {
    public T findMax(T[] array) {

	}
}

以上代码中,方法的作用就是传递一个数组进去,要求返回这个数组中的最大值。

这个时候问题就来了,泛型是T类型,当调用这个方法的时候,传递过去的参数类型不一定就是简单数据类型啊。那么这个时候该怎么进行判断大小呢???

此时这样的泛型写法的作用就是:T extends Comparable, 就叫做泛型的上界,当传递参数类型的时候,必须传递过去的参数类型必须是实现了Comparable接口的类型才可以。换句话说,传递过去的类型必须是可以进行比较的。

当我们自己定义的一个类Node,然后实现Comparable接口,就能调用如上的方法。

切记,这样写的泛型,传递过去的参数类型,必须是实现了Comparable接口的,当然也可以传递Comparable接口本身

三、类型擦除

类型擦除值得是:代码编译后,会将泛型T,全部擦除为Object类型。如下代码:

ArrayList<Integer> list = new ArrayList<>();

上面这一行代码,虽然此时写的是Integer类型的,但是在编译之后,JVM会自动地将Integer擦除为Object。即就是这样子的:ArrayList。

那么可能就会有同学疑惑了,反正都要被擦除为Object类型,那干嘛还需要泛型这个东西???

那是因为有了泛型,可以让代码在编译的时候,进行类型的检查,就像上面的代码,写的是Integer类型的,那么传递过去的参数类型就必须是Integer类型才能通过编译。做到了类型检查,且在使用get方法,获取某一个数值的时候,也会自动地将数据从Object转换为Integer类型的。

以上的全部,只是解释了只写T类型的擦除机制。那么使用泛型的上界的话,JVM就不会直接擦除为Object类型了,而是上界是什么类型,就擦除为什么类型即可。比如

public class Algorithm<T extends Comparable<T>> {

}

如上的代码,上界是Comparable接口,那么这个代码形参部分,最多也只是这个接口了,也就是说,天花板就是这个接口。那么此时JVM在擦除的时候,直接擦除为Comparable接口类型即可。

总结:类型擦除,主要还是要看类型的边界。

四、泛型类的使用-通配符

在泛型中,称为通配符。有如下代码:

public class MyArrayList<E> {
    //自定义的一个类
}

//main方法中的一个普通方法
public static void printAll(MyArrayList<?> list) {
    //打印传递过来的参数list
}

以上代码,可能大家看起来会怪怪的。简单分析一下:

MyArrayList类型,是程序员自定义的一个类,采用了泛型。

而printAll方法,需要打印MyArrayList的数据,但是MyArrayList是一个独立的java文件,而printAll方法是在main方法中。二者并没有任何联系。那么此时在调用printAll时,形参部分就没法进行定义了。

此时的话,就只能使用MyArrayList<?>类型作为形参部分,这样定义的话,此时传递什么类型的MyArrayList,printAll方法都能够进行接收。

有如下调用代码:

public static void main(String[] args) {
    MyArrayList<String> list1 = new MyArrayList<>();
    MyArrayList<Integer> list2 = new MyArrayList<>();
    MyArrayList<Double> list3 = new MyArrayList<>();

    printAll(list1); //String类型
    printAll(list2); //Integer类型
    printAll(list3); //Double类型
}

以上代码,在运行的时候就不会报错了。printAll方法,能够接收所有类型的MyArrayList类的实例对象。

通配符的上界:<? extends 上界>

public static void printAll(MyArrayList<? extends Number> list) {

}

如上代码,是在原来代码的基础之上,添加了上界。原先的代码是可以接收任何类型的MyArrayList实例对象。这里加了上界后,表示此时这个方法只能接收这个上界类型,以及上界的所有子类类型。

就拿上面这个代码来说,上界是Number类型,那么此时这个printAll方法能够接收的形参类型就只能是Number或者Number的子类类型。

public static void main(String[] args) {
    MyArrayList<String> list1 = new MyArrayList<>();
    MyArrayList<Integer> list2 = new MyArrayList<>();
    MyArrayList<Double> list3 = new MyArrayList<>();

    //printAll(list1);
    //String类型,此时String类型就会报错,因为String不是Number的子类

    printAll(list2); //Integer类型
    printAll(list3); //Double类型
}

如上代码,String类型的MyArrayList,调用printAll方法,就会报错,因为String不是Number类的子类。

同理有了通配符的上界,那么也有通配符的下界。

通配符的下界:<? super 下界>

public static void printAll(MyArrayList<? super Integer> list) {

}

同理,此时传递过去的MyArrayList实例对象的类型,只能是Integer类型,或者是Integer类型的父类。

public static void main(String[] args) {
    MyArrayList<String> list1 = new MyArrayList<>();
    MyArrayList<Integer> list2 = new MyArrayList<>();
    MyArrayList<Double> list3 = new MyArrayList<>();
    MyArrayList<Number> list4 = new MyArrayList<>();
    MyArrayList<Object> list5 = new MyArrayList<>();

    //printAll(list1);
    //此时String类型就会报错,因为String的父类并不是Integer

    printAll(list3); //Double类型
    //此时Double类型也会报错,Double类型的父类并不是Integer

    printAll(list2); //Integer类型
    printAll(list4); //Number类型
    printAll(list5); //Object类型,是所有类的父类
}

以上代码,就是通配符的下界。传递的参数类型,只能是该下界或者是下界的父类。

五、泛型方法

除了泛型类,还有一个泛型方法的概念。比较以下两个代码:

//泛型类的写法
public class Algorithm<T extends Comparable<T>> {
    public T findMax(T[] array) {

	}
}

此时我想把findMax方法,写成静态的。写成静态之后,这个方法就不依赖于对象了,只需要通过类名就能进行调用,可能你会觉得这还不简单,你就写下了以下代码:

//猜想你会写如下代码--错误写法
public class Algorithm<T extends Comparable<T>> {
    public static T findMax(T[] array) {

	}
}

上面写的代码,肯定是不对的。正确的写法如下:

//静态方法的正确写法
public class Algorithm {
    public static<T extends Comparable<T>> T findMax(T[] array) {

	}
}

如上代码,既然是写泛型方法,我们只需将原先写在类名后面的泛型,写在static关键字后面即可。这样写就是一个泛型方法。

六、泛型的限制

  • 泛型类型不支持基本数据类型,只能传递基本数据类型的包装类
  • 无法实例化泛型类型的对象。(比如new T)
  • 无法使用泛型类型声明静态的属性
  • 无法使用instanceof判断带类型参数的泛型类型
  • 无法创建泛型类的数组。(只能new Object[],然后强转)
  • 无法create、catch、throw一个泛型类异常(异常不支持泛型)
  • 泛型类型不是形参的一部分,无法重载

好啦,本期更新就到此结束啦,我们下期见吧!!!

到此这篇关于Java 泛型详解与范例的文章就介绍到这了,更多相关Java 泛型内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 深入浅出理解Java泛型的使用

    目录 一.泛型的意义 二.泛型的使用 三.自定义泛型类 1.关于自定义泛型类.泛型接口: 2.泛型在继承方面的体现 3.通配符的使用 一.泛型的意义 二.泛型的使用 1.jdk 5.0新增特性 2.在集合中使用泛型: 总结: A.集合接口或集合类在jdk5.0时都修改为带泛型的结构. B.在实例化集合类时,可以指明具体的泛型类型. C.指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法.构造器.属性等)使用类的泛型的位置, 都指定为实例化的泛型类型.比如:add(E e) --

  • Java泛型模拟scala实现自定义ArrayList方式

    目录 泛型模拟scala实现自定义ArrayList 自定义实现ArrayList代码 泛型模拟scala实现自定义ArrayList 泛型就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参), 然后在使用/调用时传入具体的类型 操作的数据类型被指定为一个参数,这种参数类型可以用在类.接口和方法中,分别被称为泛型类.泛型接口.泛型方法. 以下实例通过泛型,灵活的实现了类似scala中集合的map,reduce方法,并可以链式编程 Functi

  • Java集合框架入门之泛型和包装类

    目录 1. 预备知识-泛型(Generic) 1.1 泛型的引入 1.2 泛型的分类 1.3 泛型类的定义 1.4 泛型编译的机制 2. 预备知识-包装类(Wrapper Class) 2.1 基本数据类型和包装类的对应关系 2.2 包装类介绍 2.3 装箱(boxing)和拆箱(unboxing) 2.4 自动装箱(autoboxing)和自动拆箱(autounboxing) 2.5 包装类面试题 前言: 本章主要是为了后面学习集合框架所做的知识补充.补充了泛型以及包装类两个知识,但是该章泛型

  • 半小时通透Java的泛型

    目录 前言 学习目标 1. 什么是泛型 2. 为什么需要泛型 3. 如何使用泛型 3.1 泛型使用 3.2 自定义泛型类 3.2.1 Java 源码中泛型的定义 3.2.2 自定义泛型类实例1 3.2.3 自定义泛型类实例2 3.3 自定义泛型方法 4. 泛型类的子类 4.1 明确类型参数变量 4.2 不明确类型参数变量 5. 类型通配符 5.1 无限定通配符 5.2 extends 通配符 5.3 super 通配符 6. 小结 Java 泛型 前言 编程不能停止,每天发一篇~ 不要捉急往后学

  • Java语法关于泛型与类型擦除的分析

    泛型与类型擦除 泛型,JDK 1.5新特性,本质是参数化类型(Parametersized Type) 的应用,即所操作的数据类型被指定为一个参数.这种参数类型可用在: 类 接口 方法 的创建中, 分别称为: 泛型类 泛型接口 泛型方法 在Java还没有泛型的版本时.只能通过: Object 是所有类型的父类 类型强制转换 两个特性协作实现类型泛化.例如,在哈希表的存取中,JDK 1.5之前使用HashMap的get() 方法,返回值就是个Object.由于Java语言里面所有的类型都维承于ja

  • Java中的纸老虎之泛型

    目录 一. 泛型的定义 二. 为什么要用到泛型 三. 泛型的写法 四. 泛型的使用实例 1. 求最大值 2. 优化 五. 通配符 1. 基本写法 2. 上界 3. 下界 六. 泛型的限制 泛型,其实算是Java当中比较难的语法了,很多人一开始都对其一知半解,也很害怕阅读带泛型的源码,虽然看起来语法很难,但当你理解后会觉得很简单,其实只是一个纸老虎罢了.下面,我将会用非常简单易懂的方式带你去理解它,相信你在认真看完后会有非常大的收获,从此不会再畏惧它! 一. 泛型的定义 这里大家可以不必去看网上的

  • java基础开发泛型类的详解

    目录 前言 泛型概念 泛型类 结论 前言 在软件开发中,有许多执行过程很类似,许多人使用复制粘贴完成功能,这种做法虽然编译器不会报错,但会使用波浪线给出提示,给以后的维护带来了很大的隐患.这种情况开发人员通常根据需要成员抽取公用方法.公用类或使用继承完成,提高了代码的复用.但是,在一些特殊情况(如执行过程中会使用到对象,这些对象操作相同,但具体的模块有有所区别),此时只能使用泛型完成代码的复用. 泛型概念 所谓泛型就是将类型由原来的具体类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式

  • Java泛型在集合使用与自定义及继承上的体现和通配符的使用

    泛型的概念 集合容器类在设计阶段/声明阶段不能确定这个容器实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决.因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此把元素的类型设计成一个参数,这个类型参数叫做泛型.Collection<E>,ArrayList<E> 中<E>就是类型参数,即泛型. 所谓泛型,就是允许在定义类.接口时通过一个标识表示类中某个属性

  • Java 泛型详解与范例

    目录 一.泛型的使用 二.泛型类的定义-类型边界 三.类型擦除 四.泛型类的使用-通配符 五.泛型方法 六.泛型的限制 一.泛型的使用 前面我们学集合的时候,简单的说过泛型的使用.如下: ArrayList<Integer> list = new ArrayList<>(); Queue<Integer> queue = new LinkedList<>(); 那么使用是这样的简单,该注意什么? 尖括号里的类型,只能写引用类型 基础数据类型的话,就需要写相应

  • Java总结篇系列:Java泛型详解

    一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下面这段简短的代码: public class GenericTest { public static void main(String[] args) { List list = new ArrayList(); list.add("qqyumidi"); list.add("corn"); list.add(100); for (int i = 0; i < list.size(); i++) { S

  • 2022最新Java泛型详解(360度无死角介绍)

    目录 什么是泛型 重点概念1:泛型的作用域是在编译期间 重点概念2:泛型主要作用是在编译期间提供类型安全监测机制 泛型的使用 泛型类 泛型接口 泛型方法 泛型类中的泛型方法 泛型通配符 通配符上限 通配符下限 类型擦除 泛型与数组 小总结 什么是泛型 Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了 编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构.泛型的本质就是 参数化类型,也就是所操作的数据类型被指定为一个参数. 重点概念1:泛型的作用域是在编译

  • Java泛型详解

    1. Why --引入泛型机制的原因 假如我们想要实现一个String数组,并且要求它可以动态改变大小,这时我们都会想到用ArrayList来聚合String对象.然而,过了一阵,我们想要实现一个大小可以改变的Date对象数组,这时我们当然希望能够重用之前写过的那个针对String对象的ArrayList实现. 在Java 5之前,ArrayList的实现大致如下: public class ArrayList { public Object get(int i) { ... } public

  • 深入理解java泛型详解

    什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样. 可以在集合框架(Collection framework)中看到泛型的动机.例如,Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象. 因为 Map.get(

  • Java 泛型详解(超详细的java泛型方法解析)

    目录 2. 什么是泛型 3. 使用泛型的好处 4. 泛型的使用 4.1 泛型类 4.2 泛型方法 4.3 泛型接口 5. 泛型通配符 5.1 通配符基本使用 5.2 通配符高级使用 6. 总结 1. 为什么使用泛型 早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题.也就存在这隐患,所以Java提供了泛型来解决这个安全问题. 来看一个经典案例: public static void main(String[] args) { //测试一下泛型的经典案例 Arra

  • Java 基础详解(泛型、集合、IO、反射)

    计划把 Java 基础的有些部分再次看一遍,巩固一下,下面以及以后就会分享自己再次学习的一点笔记!不是有关标题的所有知识点,只是自己觉得模糊的一些知识点. 1.对于泛型类而言,你若没有指明其类型,默认为Object: 2.在继承泛型类以及接口的时候可以指明泛型的类型,也可以不指明: 3.泛型也数据库中的应用: 写一个 DAO 类对数据库中的数据进行增删改查其类型声明为 <T> .每张表对应一个类,对应每一张表实现一个类继承该 DAO 类并指明 DAO 泛型为该数据表对应的类,再实现一个与该表匹

  • Java的类型擦除式泛型详解

    Java选择的泛型类型叫做类型擦除式泛型.什么是类型擦除式泛型呢?就是Java语言中的泛型只存在于程序源码之中,在编译后的字节码文件里,则全部泛型都会被替换为原来的原始类型(Raw Type),并且会在相应的地方插入强制转型的代码. 因此,对于运行期间的Java程序来说ArrayList< Integer>和ArrayList< String>其实是同一个类型.这也就是Java选择的泛型类型叫做类型擦除式泛型的原因. ArrayList<String> stringAr

  • Java使用通配符实现增强泛型详解

    目录 使用通配符增强泛型 1.题目 2.解题思路 3.代码详解 知识点补充 使用通配符增强泛型 1.题目 泛型是JAVA重要的特性,使用泛型编程,可以使代码复用率提高. 实现:在泛型方法中使用通配符 2.解题思路 创建一个类:WildcardsTest. 创建一个方法getMiddle()用于获得给定列表的中间值. 在泛型中,使用“?”作为通配符,通配符的使用与普通的类型参数类似,如通配符可以利用extends关键字来设置取值的上限.如 <? extends Number> 表示Byte,Do

  • Kotlin 泛型详解及简单实例

     Kotlin 泛型详解 概述 一般类和函数,只能使用具体的类型:要么是基本类型,要么是自定义的类.如果要编写可以应用于多种类型的代码,这种刻板的约束对代码的限制很大.而OOP的多态采用了一种泛化的机制,在SE 5种,Java引用了泛型.泛型,即"参数化类型".一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参.那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用

随机推荐