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】泛型的更多相关文章

  1. JavaSE| 泛型

    泛型 泛型:对后续所有操作的类型做约束,对后续操作起作用,对之前的不起作用: 对类型进行约束:  父 ----> 子,从范围上,父范围小,子范围大:把范围小的给范围大的, JDK1.5改写了集合 ...

  2. 「JavaSE 重新出发」05.02 泛型数组列表、包装类

    泛型数组列表 ArrayList 是一个采用类型参数(type parameter)的泛型类(generic class). java ArrayList<Employee> staff ...

  3. JavaSE学习笔记(9)---集合类和泛型

    JavaSE学习笔记(9)---集合类和泛型 1.Collection集合 集合概述 在前面我们已经学习过并使用过集合ArrayList<E> ,那么集合到底是什么呢? 集合:集合是jav ...

  4. [006] - JavaSE面试题(六):泛型

    第一期:Java面试 - 100题,梳理各大网站优秀面试题.大家可以跟着我一起来刷刷Java理论知识 [006] - JavaSE面试题(六):泛型 第1问:什么是泛型? Java泛型( generi ...

  5. JavaSE复习_8 泛型程序设计

    今晚看了core Java的泛型部分,万万没有想到,当时看培训班视频入门的一带而过的泛型,有这样多的细节,整理了一下书里面提到的一些自认为的重点,方便以后观阅.由于是复习,一些基础知识跳过. △泛型类 ...

  6. javaSE(九)之泛型(Generics)

    前言 这几天分享了怎么搭建集群,这一篇给大家介绍的是泛型,在我们的很多java底层的源代码都是有很多复杂的泛型的!那什么是泛型呢? 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是 ...

  7. JavaSE笔记-泛型

    定义带泛型的类 public class Cat<T> { //可以用T定义实例变量 private T name; //可以用T定义形参 //构造器没有<> public C ...

  8. JavaSE学习总结(十六)—— 泛型与泛型应用

    一.泛型概要 泛型(Generic)的本质是类型参数化,通俗的说就是用一个占位符来表示类型,这个类型可以是String,Integer等不确定的类型,表明可接受的类型. 泛型是Java中一个非常重要的 ...

  9. JavaSE习题 继承接口和泛型

    问答题: 1.子类在什么情况下可以继承父类友好成员? 答:在同一个包内 2.子类通过怎样的方法可以隐藏继承的成员变量? 答:声明一个与父类相同变量名的成员变量 3.子类重写继承的方法原则是什么? 答: ...

  10. javase高级技术 - 泛型

    在写案例之前,先简单回顾下泛型的知识 我们知道,java属于强变量语言,使用变量之前要定义,并且定义一个变量时必须要指明它的数据类型,什么样的数据类型赋给什么样的值. 所谓“泛型”,就是“宽泛的数据类 ...

随机推荐

  1. 题解 CF736D Permutations

    link Description 现在,你有一个二分图,点数为 \(2n\). 已知这个二分图的完备匹配的个数是奇数. 现在你要知道,删除每条边后,完备匹配个数是奇数还是偶数. \(1\le n\le ...

  2. jenkins容器内安装python3

    前言 很多小伙伴可能在考虑 jenkins 拉取了 github 上的代码后,发现还越少 python3 环境,那能怎么办呢? 咨询了一位运维朋友给我的答案是,将 python3 挂载到容器工作目录上 ...

  3. Oracle-绑定执行计划

    一.绑定执行计划 Oracle存在某个SQL多个执行计划的情况,那么如何快速将Oracle 好的执行计划,绑定到不好的SQL上呢? 由于版本的演进,绑定执行计划在10g 一般使用sql profile ...

  4. dubbo注册中心占位符无法解析问题

    dubbo注册中心占位符无法解析问题 1.背景 最近搞了2个老项目,想把他们融合到一起.这俩项目情况简介如下: 项目一:基于SpringMVC + dubbo,配置读取本地properties文件,少 ...

  5. Linux命令(二)

    1.cd命令 这是一个非常基本,也是大家经常需要使用的命令,它用于切换当前目录,它的参数是要切换到的目录的路径,可以是绝对路径,也可以是相对路径.如: cd /root/Docements # 切换到 ...

  6. Golang通脉之函数

    函数是组织好的.可重复使用的.用于执行指定任务的代码块. Go语言中支持函数.匿名函数和闭包,并且函数在Go语言中属于"一等公民". 函数定义 Go语言中定义函数使用func关键字 ...

  7. 占位符,SQL注入?

    这两天在上课时被同学拿了一段代码问我,这段代码有什么问题,我看了一会说:Connection和PreparedStatement都没关.他说不止这方面的问题,还有sql注入的问题,我就坚决的说使用了占 ...

  8. 表单编辑时el-form的validate方法执行无效,阻塞代码运行 - Element UI踩坑记录

    今天在用element-ui写管理后台需求时,遇到一个奇怪的问题 一个正常带校验的表单,在新增列表数据时表单校验功能正常: 但是在新增之后再去编辑数据时,表单校验却失效了,甚至阻塞了后续的代码执行,控 ...

  9. 热身训练4 Article

    Article 在这个学期即将结束时,DRD开始写他的最后一篇文章. DRD使用著名的Macrohard的软件World来写他的文章. 不幸的是,这个软件相当不稳定,它总是崩溃. DRD需要在他的文章 ...

  10. 2021.9.20考试总结[NOIP模拟57]

    (换个编辑器代码就SB地不自动折叠了.. T1 2A 考察快读的写法. $code:$ T1 #include<bits/stdc++.h> #define scanf SCANF=sca ...