所谓泛型,就是允许在定义类、接口、方法时使用类型形参,这个类型形参(或者叫泛型)将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数也可称为类型实参)
Java5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参。
Java5改写后List接口、Iterator接口、Map的代码片段:

// 定义接口时指定了一个泛型形参,该形参名为E
public interface List<E>
{// 在接口里,E可作为类型使用// 下面方法可以使用E作为参数类型void add(E x);Iterator<E> iterator();...
}
// 定义接口时指定了一个泛型形参,该形参名为E
public interface Iterator<E>
{// 在该接口里E完全可以作为类型使用E next();boolean hasNext();...
}
// 定义接口时指定了两个泛型形参, 其形参名为K、V
public interface Map<K, V>
{// 在该接口里K、V完全可以作为类型使用Set<K> keySet();V put(K key, V value);...
}

三个接口声明是比较简单的,除了尖括号中的内容,而这就是泛型的实质:允许在定义接口、类时声明泛型形参,泛型形参在整个接口、类体内可以当成类型使用,几乎所有可使用普通类型的地方都可以使用这种泛型形参
除此之外:Iterator<E> iterator(); 和 Set<K> keySet();方法声明返回值类型是Iterator、Set,这表明他们是一种特殊的数据类型,是一种与Interator、Set不同的数据类型,可以当成他们的子类看待。
例如使用List类型时,如果为E形参传入String类型实参,则产生了一个新的类型:List类型,可以把List想象成E被全部替换成String的特殊List自接口

// List<String>等同于如下接口
public interface ListString extends List
{// 原来的E形参全部变成String类型实参void add(String x);Iterator<String> iterator();...
}

虽然程序只定义了一个List接口,但实际使用时可以产生无数多个List接口,只要为E传入不同的类型实参,系统就会多处一个新的List子接口
包含泛型声明的类型可以在定义变量、创建对象时传入一个类型实参,从而可以动态地生成无数多个逻辑上的子类、但这种子类在物理上并不存在

创建泛型类、接口

可以为任何类、接口增加泛型声明

import static java.lang.System.*;// 定义Apple类时使用了泛型声明
public class Apple<T>
{// 使用T类型定义实例变量private T info;public Apple(){}// 下面方法中使用T类型来定义构造器public Apple(T info){this.info = info;}public void setInfo(T info){this.info = info;}public T getInfo(){return this.info;}public static void main(String[] args){// 由于传给T形参的是String,所以构造器参数只能是StringApple<String> a1 = new Apple<>("苹果");out.println(a1.getInfo());// 由于传给T形参的是Double,所以构造器参数只能是Double或doubleApple<Double> a2 = new Apple<>(5.67);out.println(a2.getInfo());}
}

代码中定义了一个带泛型声明的Apple类,不用管这个泛型形参是否具有实际意义,使用Apple类时就可以为T形参传入实际类型,这样就可以生成如Apple、Apple…形式的多个逻辑子类,物理上并不存在。
当创建带泛型声明的自定义类,为该类定义构造器时,构造器名还是原来的类名,不要增加泛型声明,例如为Apple类定义构造器,其构造器名依然是Apple,而不是Apple,调用该构造器时却可以使用Apple的形式,当然应该为T形参传入实际的类型参数

泛型类派生子类

当创建了带泛型声明的接口、父类之后,可以为该接口创建实现类,或从该父类派生子类,需要指出的是,当使用这些接口、父类时不能再包含泛型形参,错误的示范:

// 定义类A集成Apple类,Apple类不能跟泛型形参
public class A extends Apple<T>{ }

定义类、接口、方法时可以声明泛型形参,使用类、接口、方法时应该为泛型形参传入实际的类型
如果想从Apple类派生一个子类,则可以:

// 使用Apple类时为T形参传入String类型
public class A extends Apple<String>

调用方法时必须为所有的数据形参传入参数值,与调用方法不同的是,使用类、接口时也可以不为泛型形参传入实际的类型参数,如下代码也是正确的:

// 使用Apple类时,没有为T形参传入实际的类型参数
public class A extends Apple

这样的用法省略了泛型的形式被称为原始类型(raw type),而如果从Apple类派生子类,则Apple类中所有使用T类型的地方都将被替换成String类型,即它的子类将会继承到String getInfo()和void setInfo(String info)两个方法

public class A1 extends Apple<String>
{// 正确重写了父类的方法,返回值// 与父类Apple<String>的返回值完全相同public String getInfo(){return "子类" + super.getInfo();}/*// 下面方法是错误的,重写父类方法时返回值类型不一致public Object getInfo(){return "子类";}*/
}

如果使用Apple类时没有传入实际的类型(即使用原始类型),Java编译器可能发出警告:使用了未经检查或不安全的操作,如果希望看到该警告提示更详细信息,则可以通过微javac命令增加-Xlint:unchecked选项来实现。
没有传入实际类型,则系统会把Apple类里的T形参当成Object类型处理

public class A2 extends Apple
{// 重写父类的方法public String getInfo(){// super.getInfo()方法返回值是Object类型,// 所以加toString()才返回String类型return super.getInfo().toString();}
}

创建带泛型声明的接口的实现类与此几乎完全一样

剖析泛型类

虽然说可以把ArrayList类当成ArrayList的子类,而实际上ArrayList类也确实是一种特殊的ArrayList类,该ArrayList对象只能添加String对象作为集合元素,但实际上系统并没有为ArrayList生成新的class文件,而且也不会把ArrayList当成新类来处理

// 创建List<String>对象和List<Integer>对象
List<String> strList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
// 调用getClass()方法比较两个对象的类是否相等
System.out.println(strList.getClass() == intList.getClass());

结果返回true,不管泛型的世纪类型参数是什么,他们在运行时总是有同样的类(class),在内存中也只占一块内存空间,因此静态方法、静态初始化块、静态变量的声明和初始化中不允许使用泛型形参

public class R<T>
{// 下面代码错误,不能在静态变量声明中使用泛型形参//    static T info;T age;public void foo(T msg){}// 下面代码错误,不能在静态方法声明中使用泛型形参// public static void bar(T msg){}}

因为系统不会真正的生成泛型类,所有instanceof运算符后不能使用泛型类,如下是错误的示范:

java.util.Collection<String> cs - new java.util.ArrayList<>();
// 下面代码编译时会报错:instanceof运算符后不能使用泛型
if (cs instanceof java.util.ArrayList<String>{...})

Java面向对象系列[v1.0.0][泛型进阶]相关推荐

  1. Java面向对象系列[v1.0.0][泛型基础]

    Java5增加泛型支持,很大程度上是为了让集合记住其元素的数据类型,在此之前不用泛型的话,一旦把一个对象放进Java集合中,集合就会忘记对象的类型,把所有的对象当成Object类型处理,从集合里取出对 ...

  2. Java面向对象系列[v1.0.0][类加载器]

    类加载器负责将.class文件加载到内存中,并为它生成对应的java.lang.Class对象 类加载机制 类加载器负责加载所有的类,并未所有被载入内存中的类生成一个java.lang.Class实例 ...

  3. Java面向对象系列[v1.0.0][SQL语法之DDL]

    SQL语法基础 SQL全称是Structured Query Language也就是结构化查询语言,它是操作和检索关系数据库的标准语言,标准的SQL语句可以用于操作任何关系数据库,标准的SQL语句通常 ...

  4. Java面向对象系列[v1.0.0][SQL语法之数据库约束]

    数据库约束 所有关系型数据库都支持对数据表使用约束,在表面上是强制执行的数据校验规则,通过约束可以更好的的保证数据表里数据的完整性 大部分数据库支持5中完整性约束 NOT NULL:非空约束,指定某列 ...

  5. Java自动化测试系列[v1.0.0][TestNG测试开发环境配置]

    基于之前写的一篇文章Java自动化测试系列[v1.0.0][Maven开发环境]的基础上,阐述如何配置单元测试框架TestNG的测试开发环境 创建Maven项目 启动IDEA,点击Create New ...

  6. 阿里巴巴Java开发手册v1.2.0版

    最近在忙毕设,读了阿里java开发手册后,发现可以更好的读懂别人的代码了,一些文件之间的关系通过命名就能看出了.对自己养成良好的编码风格也有极大的帮助. 本手册仅供大家交流.学习及研究使用,禁止用于商 ...

  7. 阿里巴巴Java开发手册v1.2.0网页版

    前言 一 .编程规约 ( 一 ) 命名风格 ( 二 ) 常量定义 ( 三 ) 代码格式 ( 四 ) OOP规约 ( 五 ) 集合处理 ( 六 ) 并发处理 ( 七 ) 控制语句 ( 八 ) 注释规约 ...

  8. Java自动化测试系列[v1.0.0][自动化测试框架理念]

    自动化测试含义 在自动化测试领域内流传着一个说法:单元测试才是自动化测试的核心,在自动化测试里,无论框架何等完美都不可能脱离单元测试,单元测试将会是自动化测试里最小的单位,把它看作单位一,若干个单位一 ...

  9. Java自动化测试系列[v1.0.0][ZTestReport测试报告]

    获取报告框架 ZTestReport 源码Clone地址为ZTestReport ,其中ZTestReport.java和其template是我们需要的关键 修改报告框架 根据我的需要,在源码基础上进 ...

最新文章

  1. 自动驾驶的视觉感知包括哪些内容?
  2. Android系统在新进程中启动自定义服务过程(startService)的原理分析 (下)
  3. VUE指令篇_不常用指令
  4. SAP CRM 和 SAP Cloud for Customer 的表格列项目宽度调整的原理介绍
  5. 2020年,朋友圈的正确打开方式!
  6. jms activemq_带有ActiveMQ的JMS
  7. 中大计算机考研复试刷人太狠,中山大学考研复试被刷原因大盘点!
  8. 《树莓派实战秘籍》——1.17 技巧17添加重启按钮
  9. python psycopg2使用_python 操作数据库:psycopg2的使用
  10. taro更新页面数据_Taro 全局数据状态管理
  11. java中输出红字_使用JDIC实现Java界面嵌入Web浏览器 出红字
  12. PLY——Python Lex Yacc
  13. 浏览器实现word在线预览
  14. Steam游戏导入vrPlus中运行
  15. THULAC 词性表
  16. Lecture05:随机市场出清
  17. 盲打打字php,盲打26键打字口诀是什么
  18. RISCV学习笔记7.8(开源虚拟机篇)--AlmaLinux虚拟机安装modelsim
  19. mysql 连接时间_MySQL连接时间问题
  20. project2016设置项目更新与监控方法

热门文章

  1. LeetCode(困难)地下城游戏(c#)
  2. 看看IT高手是怎样泡妞的!(转)
  3. Linux 卸载OpenJDK
  4. 仓库管理信息系统的设计与实现
  5. 倍福FTP文件下载方式
  6. linux系统进程调度算法,Linux系统中的进程调度
  7. vue鼠标的移入移除事件
  8. to B和to C是什么意思?它们有什么区别?
  9. ubuntu 热键设置_如何在Ubuntu中使用热键关闭显示器
  10. python使用json模块还需要安装吗_python json模块安装