【JavaSE】泛型
Java泛型
2019-07-05 22:00:24 by冲冲
1. 泛型的引例
- 1 List list = new ArrayList();
- 2 list.add(1022); //向集合中添加一个 Integer 类型的数据
- 3 list.add("Yadiel"); //向集合中添加一个 String 类型的数据
- 4
- 5 //遍历list元素
- 6 for(int i = 0 ; i < list.size() ; i++){
- 7 Object obj = list.get(i); //注意这里每个类型都是 Object
- 8 System.out.println(obj);
- 9 }
- 10
- 11 //如果遍历的时候就想得到自己想要的数据类型
- 12 for(int i = 0 ; i < list.size() ; i++){
- 13 String obj = (String) list.get(i); //在取 Integer 的时候会报类型转换错误
- 14 System.out.println(obj);
- 15 }
报错信息:
意思是 集合中第二个数据是 Integer,但是我们取出来的时候将其转换为 String 了,所以报错。
解决方案:
① 我们在遍历的时候,根据每个数据的类型判断,然后进行强转。缺陷是,如果数据成千上万,该方案不可取。
② 在往集合中加入数据的时候,就做好限制,比如这个集合只能添加 String 类型的,下一个集合只能添加 Integer 类型的(就像数组一样规定元素类型必须相同),那么在取数据时,由于前面已经限制了该集合的数据类型,那么就很好强转了。这第二种解决办法,也就是我们这篇文章讲的 泛型 。
2. 泛型的概念
泛型是Java SE1.5的新特性。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
3. 泛型的语法
(1)引例的解决
- 1 List<String> list = new ArrayList<String>();
- 2 //list.add(1022); //向集合中添加一个 Integer 类型的数据时,编译器会报错
- 3 list.add("Aoa"); //向集合中添加一个 String 类型的数据
- 4 list.add("Loa"); //向集合中添加一个 String 类型的数据
- 5
- 6 //如果我们遍历的时候就想得到自己想要的数据类型
- 7 for(int i = 0 ; i < list.size() ; i++){
- 8 String obj = list.get(i); //这里就不需要强转了,前面添加的是什么类型,这里获取的就是什么类型
- 9 System.out.println(obj);
- 10 }
(2)泛型只在编译阶段有效
- 1 List<String> list1 = new ArrayList<String>();
- 2 List list2 = new ArrayList();
- 3 Class c1 = list1.getClass();
- 4 Class c2 = list2.getClass();
- 5 System.out.println(c1==c2); //true
由于反射是在运行时阶段,c1==c2为 true,说明编译之后的 class 文件中是不包含泛型信息。
结论:Java 泛型只在编译阶段有效,即在编译过程中,程序会正确的检验泛型结果。而编译成功后,class 文件是不包含任何泛型信息的。
(3)泛型类
在类名后面添加 类型参数 的声明部分,其他跟非泛型类完全一样。
泛型类的类型参数声明部分,可以包含一个或多个类型参数,参数间用逗号隔开。
- 1 public class Box<T> {
- 2 private T t;
- 3 public void add(T t) {
- 4 this.t = t;
- 5 }
- 6 public T get() {
- 7 return t;
- 8 }
- 9
- 10 public static void main(String[] args) {
- 11 Box<Integer> integerBox = new Box<Integer>();
- 12 Box<String> stringBox = new Box<String>();
- 13
- 14 integerBox.add(new Integer(1022));
- 15 stringBox.add(new String("肥猪"));
- 16
- 17 System.out.println("整型值为 :"+ integerBox.get());
- 18 System.out.println("字符串为 :"+ stringBox.get());
- 19 }
- 20 }
输出结果:
- 1 整型值为 :1022
- 2 字符串为 :肥猪
(4)泛型方法
应用场景:假如需要写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?答案是泛型。
定义泛型方法的规则:
① 所有泛型方法声明,都有一个 类型参数 的声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的<E>)。
② 每一个 类型参数 声明部分可以包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
③ 类型参数 能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
④ 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char等)。
- 1 public class GenericMethodTest { //演示如何使用泛型方法打印不同字符串的元素
- 2 // 泛型方法 printArray
- 3 public static <E> void printArray(E[] inputArray) {
- 4 // 输出数组元素
- 5 for (E element : inputArray) {
- 6 System.out.printf("%s ", element);
- 7 }
- 8 System.out.println();
- 9 }
- 10
- 11 public static void main(String args[]) {
- 12 // 创建不同类型数组: Integer, Double 和 Character
- 13 Integer[] intArray = { 1, 2, 3, 4, 5 };
- 14 Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
- 15 Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
- 16
- 17 System.out.println("整型数组元素为:");
- 18 printArray(intArray); // 传递一个整型数组
- 19
- 20 System.out.println("\n双精度型数组元素为:");
- 21 printArray(doubleArray); // 传递一个双精度型数组
- 22
- 23 System.out.println("\n字符型数组元素为:");
- 24 printArray(charArray); // 传递一个字符型数组
- 25 }
- 26 }
输出结果:
- 1 整型数组元素为:
- 2 1 2 3 4 5
- 3
- 4 双精度型数组元素为:
- 5 1.1 2.2 3.3 4.4
- 6
- 7 字符型数组元素为:
- 8 H E L L O
(5)类型通配符?
① 类型通配符 通常使用 ? 代替具体的类型参数。例如 List<?> 在逻辑上是 List<String>,List<Integer> 等所有List<具体类型实参>的父类。
- 1 import java.util.*;
- 2
- 3 public class GenericTest {
- 4
- 5 public static void main(String[] args) {
- 6 List<String> name = new ArrayList<String>();
- 7 List<Integer> age = new ArrayList<Integer>();
- 8 List<Number> number = new ArrayList<Number>();
- 9
- 10 name.add("yadiel");
- 11 age.add(18);
- 12 number.add(999);
- 13
- 14 getData(name);
- 15 getData(age);
- 16 getData(number);
- 17
- 18 }
- 19
- 20 public static void getData(List<?> data) {
- 21 System.out.println("data :" + data.get(0));
- 22 }
- 23 }
输出结果:
- 1 data :yadiel
- 2 data :18
- 3 data :999
② 类型通配符的上限和下限
上限:<? extends T> 表示该通配符所代表的类型是 T类型 或者 T类型的子类。
下限:<? super T> 表示该通配符所代表的类型是 T类型 或者 T类型的父类。
- 1 import java.util.*;
- 2
- 3 public class GenericTest {
- 4
- 5 public static void main(String[] args) {
- 6 List<String> name = new ArrayList<String>();
- 7 List<Integer> age = new ArrayList<Integer>();
- 8 List<Number> number = new ArrayList<Number>();
- 9
- 10 name.add("yadiel");
- 11 age.add(18);
- 12 number.add(999);
- 13
- 14 //getUperNumber(name);//String类型表示Number类型的子类,报错
- 15 getUperNumber(age); //输出18
- 16 getUperNumber(number);//输出999
- 17
- 18 }
- 19
- 20 public static void getUperNumber(List<? extends Number> data) {
- 21 System.out.println("data :" + data.get(0));
- 22 }
- 23 }
设计泛型方法,返回三个可比较对象的最大值。
- 1 public class MaximumTest
- 2 {
- 3 // 比较三个值并返回最大值
- 4 public static <T extends Comparable<T>> T maximum(T x, T y, T z)
- 5 {
- 6 T max = x; // 假设x是初始最大值
- 7 if ( y.compareTo( max ) > 0 ){
- 8 max = y; //y 更大
- 9 }
- 10 if ( z.compareTo( max ) > 0 ){
- 11 max = z; // 现在 z 更大
- 12 }
- 13 return max; // 返回最大对象
- 14 }
- 15 public static void main( String args[] )
- 16 {
- 17 System.out.printf( "%d, %d 和 %d 中最大的数为 %d\n\n",
- 18 3, 4, 5, maximum( 3, 4, 5 ) );
- 19
- 20 System.out.printf( "%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n\n",
- 21 6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );
- 22
- 23 System.out.printf( "%s, %s 和 %s 中最大的数为 %s\n","pear",
- 24 "apple", "orange", maximum( "pear", "apple", "orange" ) );
- 25 }
- 26 }
输出结果:
- 1 3, 4 和 5 中最大的数为 5
- 2
- 3 6.6, 8.8 和 7.7 中最大的数为 8.8
- 4
- 5 pear, apple 和 orange 中最大的数为 pear
4. 注意事项
① 不能用基本类型来定义泛型,如 int、float。
- 1 List<int> list = new ArrayList<int>(); //不能用 int 这样的基本类型定义泛型
因为集合中只能存放引用类型的数据,即使你存入基本类型,Java还是会通过自动拆箱和自动装箱机制将其转换为引用类型。
② 如果使用 ? 接收泛型对象时,则不能设置被泛型指定的内容。
- 1 List<?> list = new ArrayList<>();
- 2 list.add("aa"); //错误,无法设置
③ 泛型方法的定义与其所在的类是否是 泛型类 是没有任何关系的,所在的类可以是泛型类,也可以不是泛型类。
④ 泛型类没有继承关系,即 String 为 Object 类的子类,则 List<String> 是 List<Object> 的子类这句话是错误的。
正确:List<String>==List<Object>。 原因:泛型只是规定了List的元素类型,如果不符合,会在编译阶段报错。在运行阶段,无论List的元素是什么类型,List的类型都属于List。
- 假设上面那句话是正确的,那么由于泛型的产生机制就是放什么类型的数据进去,取出来的就是什么类型,而不用进行类型转换。
- 这里把 String 类型的数据放入Object 类的泛型集合中,那么取出来的应该就是 String 类的数据,而实际上取出来的是 Object 类的数据,这与泛型的产生机制相违背,故不成立!
参考:
https://www.cnblogs.com/ysocean/p/6826525.html
https://www.runoob.com/java/java-generics.html
【JavaSE】泛型的更多相关文章
- JavaSE| 泛型
泛型 泛型:对后续所有操作的类型做约束,对后续操作起作用,对之前的不起作用: 对类型进行约束: 父 ----> 子,从范围上,父范围小,子范围大:把范围小的给范围大的, JDK1.5改写了集合 ...
- 「JavaSE 重新出发」05.02 泛型数组列表、包装类
泛型数组列表 ArrayList 是一个采用类型参数(type parameter)的泛型类(generic class). java ArrayList<Employee> staff ...
- JavaSE学习笔记(9)---集合类和泛型
JavaSE学习笔记(9)---集合类和泛型 1.Collection集合 集合概述 在前面我们已经学习过并使用过集合ArrayList<E> ,那么集合到底是什么呢? 集合:集合是jav ...
- [006] - JavaSE面试题(六):泛型
第一期:Java面试 - 100题,梳理各大网站优秀面试题.大家可以跟着我一起来刷刷Java理论知识 [006] - JavaSE面试题(六):泛型 第1问:什么是泛型? Java泛型( generi ...
- JavaSE复习_8 泛型程序设计
今晚看了core Java的泛型部分,万万没有想到,当时看培训班视频入门的一带而过的泛型,有这样多的细节,整理了一下书里面提到的一些自认为的重点,方便以后观阅.由于是复习,一些基础知识跳过. △泛型类 ...
- javaSE(九)之泛型(Generics)
前言 这几天分享了怎么搭建集群,这一篇给大家介绍的是泛型,在我们的很多java底层的源代码都是有很多复杂的泛型的!那什么是泛型呢? 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是 ...
- JavaSE笔记-泛型
定义带泛型的类 public class Cat<T> { //可以用T定义实例变量 private T name; //可以用T定义形参 //构造器没有<> public C ...
- JavaSE学习总结(十六)—— 泛型与泛型应用
一.泛型概要 泛型(Generic)的本质是类型参数化,通俗的说就是用一个占位符来表示类型,这个类型可以是String,Integer等不确定的类型,表明可接受的类型. 泛型是Java中一个非常重要的 ...
- JavaSE习题 继承接口和泛型
问答题: 1.子类在什么情况下可以继承父类友好成员? 答:在同一个包内 2.子类通过怎样的方法可以隐藏继承的成员变量? 答:声明一个与父类相同变量名的成员变量 3.子类重写继承的方法原则是什么? 答: ...
- javase高级技术 - 泛型
在写案例之前,先简单回顾下泛型的知识 我们知道,java属于强变量语言,使用变量之前要定义,并且定义一个变量时必须要指明它的数据类型,什么样的数据类型赋给什么样的值. 所谓“泛型”,就是“宽泛的数据类 ...
随机推荐
- 题解 「THUPC 2017」小 L 的计算题 / Sum
题目传送门 题目大意 给出 \(a_{1,2,...,n}\),对于 \(\forall k\in [1,n]\) ,求出: \[\sum_{i=1}^{n}a_i^k \] \(n\le 2\tim ...
- 洛谷4234最小差值生成树 (LCT维护生成树)
这也是一道LCT维护生成树的题. 那么我们还是按照套路,先对边进行排序,然后顺次加入. 不过和别的题有所不同的是: 在本题中,我们需要保证LCT中正好有\(n-1\)条边的时候,才能更新\(ans\) ...
- CAD_DWG图Web可视化一站式解决方案-唯杰地图-vjmap
背景 DWG图是AutoCAD是私有格式,只能在CAD软件上编辑查看,如何发布至Web上做数据展示,GIS分析应用开发,一直是业内头疼的事情. 传统的办法采用的解析AutoCAD图形绘制,并封装成Ac ...
- Using C++ in VS Code
Using C++ in VS Code Get Started with C++ and Windows Subsystem for Linux in Visual Studio Code ...
- 手把手教你写hexo博客
市面上现在有各种博客框架,本博客教大家的是Hexo博客框架,目前比较火.搭建博客中遇到各种各样问题,网上方案也比较成熟. 一.搭建环境 安装 git 安装 node.js 安装 Hexo npm in ...
- STM32中操作寄存器GPIOB_CRL &= ~( 0x0F<< (4*0))与GPIOB_CRL &=~(0x0F)之间有什么区别吗?
没有区别,作用相同.只是这样写便于修改和沿用. 对于只用到PB0端口的程序~(0x0f << (4*0)) 和~0x0f没有区别.0x0f <<(4*N) 就是 向左 移动N个 ...
- 洛谷 P5658 [CSP-S2019] 括号树
链接: P5658 分析: 显然我们应该在dfs树的同时维护每个点的答案. 注意到第 \(u\) 个点的答案可以分成两部分,不包含 \(u\) 点时的答案,和加入 \(u\) 点后新增的答案,前者可以 ...
- linux 文件描述符和inode 的理解和区别
inode 或i节点是指对文件的索引.如一个系统,所有文件是放在磁盘或flash上,就要编个目录来说明每个文件在什么地方,有什么属性,及大小等.就像书本的目录一样,便于查找和管理.这目录是操作系统需要 ...
- 【数据结构&算法】05-线性表之数组
目录 前言 线性结构与非线性结构 数组 数组的两个限制 数组的随机访问特性 数组的操作 插入操作 删除操作 数组越界 容器 数组下标 前言 本笔记主要记录数组的一些基础特性及操作. 顺便解答下为什么大 ...
- matlab与python scipy.signal中 freqs freqz 中w,什么时候是角频率,什么时候是真实的工程中使用的采样频率Hz,如何转化
matlab与python scipy.signal中的freqs,freqz频率分析函数,输出的w,有时候是角频率,有时候是真实频率,容易搞混,这里对比一下. 0. 精要总结: 1) freqs: ...