JAVA基础_泛型
什么是泛型
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉”类型”信息,是程序的运行效率不受影响,对于参数化的泛型类型,getClass()方法返回值和原始类型完全一样。由于编译生成的字节码会擦除泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,然后再调用add()方法即可
GenericDemo.java
public class GenericDemo {
public static void main(String[] args) {
// 未使用泛型,则可以添加任意Object类型的数据
List noGrnerics = new ArrayList<>();
noGrnerics.add(1);
noGrnerics.add("hello");
noGrnerics.add("1L");
noGrnerics.add(new GenericDomain(1L,"Generic"));
// 由于没有使用泛型,在取出数据时必须要知道某个位置上的某个数据的数据类型,并加以强制转换,否则要出错,除非用Object接收。
Integer integer = (Integer) noGrnerics.get(0);
System.out.println("integer = " + integer);
// 由于不知道某个位置上的数据类型,用Object接收
Object objIndex1 = noGrnerics.get(1);
System.out.println("objIndex1 = " + objIndex1);
Object objeIndex3 = noGrnerics.get(3);
System.out.println("objeIndex3 = " + objeIndex3);
// 虽然可以通过类对象来知道类属性,但是这样不是太麻烦了吗??
System.out.println("objeIndex3.getClass() = " + objeIndex3.getClass());
// 使用泛型
List<String> strs = new ArrayList<>();
strs.add("str1");
strs.add("str2");
strs.add("str3");
// 因为指定了这个List中只能放置String类型的数据,当这里放置其他类型的数据时就会报错,因为不能装进去
// 从某种角度上可以认为泛型解决了强制类型转换,使数据特征更明显,拥有一致性
// strs.add(1);
String str = strs.get(0);
System.out.println("str = " + str);
}
}
绕过编译时泛型,添加非泛型类型数据:GenericDemo2.java
public class GenericDemo2 {
/**
* 程序目标,向一个List<String>的泛型数组中添加Integer类型的数据
*
* @param args
*/
public static void main(String[] args) {
List<String> strs = new ArrayList<>();
strs.add("str1");
strs.add("str2");
strs.add("str3");
strs.add("str4");
// 这里是添加不进去的,因为指定了泛型类型,且没有泛型擦除
// strs.add(new Integer(1));
System.out.println("Before add a Integer value : " + strs);
// 通过泛型在运行时擦除的原理,利用反射向泛型中添加Integer数据
Class<List<String>> clazz = (Class<List<String>>) strs.getClass();
try {
// 这里在利用反射获取add方法时,如果参数类型指定为String.class,
// 则说明add方法为,java.util.ArrayList.add(java.lang.String)
// 而我们传入了一个Integer类型的数据,则显然不能添加成功,会抛出 java.lang.NoSuchMethodException
clazz.getDeclaredMethod("add", String.class).invoke(strs, new Integer(1));
// 这里原因一样,java.util.ArrayList.add(java.lang.Integer) 但是用的int,
// 请记住,java是强类型语言,这里不存在自动装箱和拆箱
clazz.getDeclaredMethod("add", Integer.class).invoke(strs, 2);
// 这里也是不正确的,因为Class<List<String>>指定了放String
clazz.getDeclaredMethod("add", Integer.class).invoke(strs, new Integer(4));
//同上
clazz.getDeclaredMethod("add", int.class).invoke(strs, 5);
} catch (Exception e) {
e.printStackTrace();
} finally {
//正确做法
try {
clazz.getDeclaredMethod("add", Object.class).invoke(strs, 3);
System.out.println("strs = " + strs);
// 只有Object能行
clazz.getDeclaredMethod("add", String.class).invoke(strs, "4");
System.out.println("strs = " + strs);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("After add a Integer value : " + strs);
}
}
解释为什么只能用Object,运行时会擦除泛型,下面的代码清单就可以验证
public class GenericTypeDemo {
public static void main(String[] args) {
// 这里的字节码类型为ArrayList
List<String> strings = new ArrayList<>();
// 这里的字节码类型也为ArrayList
List<Integer> integers = new ArrayList<>();
System.out.println("strings.getClass() = " + strings.getClass());
System.out.println("integers.getClass() = " + integers.getClass());
System.out.println("integers.getClass() == strings.getClass() = "
+ (integers.getClass() == strings.getClass()));
}
}
泛型中的?通配符
- 需求:定义一个方法,该方法用于打印出任意参数类型化的集合中的所有数据。
public class GenericDemo3 {
public static void main(String[] args) {
// 需求:定义一个方法,该方法用于打印出任意参数类型化的集合中的所有数据。
List<String> strings = new ArrayList<>();
strings.add("a");
strings.add("b");
strings.add("c");
strings.add("f");
printCollectionElements(strings);
printStringCollectionElements(strings);
List<Integer> integers = new ArrayList<>();
integers.add(1);
integers.add(2);
integers.add(3);
integers.add(4);
printCollectionElements(integers);
// 这里就不能用了
// printStringCollectionElements(integers);
}
/**
* 定义一个方法,该方法用于打印出任意参数类型化的集合中的所有数据。
* 这种是通配方法,能够打印所有的,其通配之处就在与?,?可以引用其他各种参数化的类型
* @param collection
*/
public static void printCollectionElements(Collection<?> collection) {
collection.forEach(System.out::println);
}
/**
* 打印String类型的集合数据,由于制定了泛型类型为String,则只能打印String类型的集合
* @param collection
*/
public static void printStringCollectionElements(Collection<String> collection) {
collection.forEach(System.out::println);
}
}
- 总结
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,如size(),不能调用与参数化有关的方法,如add(E e)
泛型中?的扩展
- 限定通配符的上边界
- 正确 List<? extends Number> x = new ArrayList<Integer>();
- 错误 List<? extends Number> x = new ArrayList<String>();
- 限定通配符的下边界
- 正确 Vector<? super Integer> x = new Vector<Number>();
- 错误 Vector<? super Integer> x = new Vector<Byte>();
- 总结
- 限定通配符总是包括自己
JAVA基础_泛型的更多相关文章
- 黑马程序员:Java基础总结----泛型(高级)
黑马程序员:Java基础总结 泛型(高级) ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! 泛型(高级) 泛型是提供给javac编译器使用的,可以限定集合中的输入类型 ...
- JAVA基础_自定义泛型
泛型的来源 在Java中,泛型借鉴了C++的模版函数,从而引入了泛型. C++泛型 int add(int x,int y){ return x + y; } float add(float x.fl ...
- 黑马程序员——JAVA基础之泛型和通配符
------- android培训.java培训.期待与您交流! ---------- 泛型: JDK1.5版本以后出现新特性.用于解决安全问题,是一个类型安全机制. 泛型好处: ...
- Java基础知识--泛型
什么是泛型?为什么使用泛型? 泛型,就是参数化类型.提到参数,最熟悉的就是定义方法时候的形参,然后调用此方法时传递实参.顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也 ...
- JAVA基础_反射获取泛型参数类型
我经常会想获取参数的实际类型,在Hibernate中就利用的这一点. domain: Person.java public class Person { // 编号 private Long id; ...
- Java基础:泛型
Java的泛型是什么呢, 就是类型的參数化,这得类型包含方法參数和返回值.也就是原本该是确定类型的地方换成了变量,把类型的确定时间向后延迟了. 在之前,学过"重载"的概念,重载是什 ...
- Java基础教程——泛型
泛型 Generics:泛型,愿意指"无商标的". 泛型,可以理解为"宽泛的数据类型",就是将类型由原来的具体的类型泛化. 泛型在建立对象时不指定类中属性的具体 ...
- 【Java基础】泛型
Num1:请不要在新代码中使用原生类型 泛型类和接口统称为泛型.每种泛型定义一组参数化的类型,构成格式是:类或接口名称,接着用<>把对应于泛型形式类型的参数的实际参数列表括起来.比如:Li ...
- 黑马程序员——【Java基础】——泛型、Utilities工具类、其他对象API
---------- android培训.java培训.期待与您交流! ---------- 一.泛型 (一)泛型概述 1.泛型:JDK1.5版本以后出现的新特性,用于解决安全问题,是一个类型安全机制 ...
随机推荐
- zabbix active模式以及自定义key not Supported的解决
zabbix active模式 active模式适用场景 zabbix server端无法直连agent端,比如agent为内网机器,仅有内网ip,没有公网ip,但是内网机器能够访问server端 a ...
- "\r\n"与"</br>"的区别
\n是换行,英文是New line,表示使光标到行首 \r是回车,英文是Carriage return,表示使光标下移一格 \r\n表示回车换行 \\ 反斜杠 \$ 美圆符 \" 双引 ...
- Alibaba Cluster Data 开源:270GB 数据揭秘你不知道的阿里巴巴数据中心
打开一篇篇 IT 技术文章,你总能够看到“大规模”.“海量请求”这些字眼.如今,这些功能强大的互联网应用,都运行在大规模数据中心上,然而,对于大规模数据中心,你又了解多少呢?实际上,除了阅读一些科技文 ...
- 基于bootstrap的时间选择插件daterangepicker以及汉化方法
双日历时间段选择插件 — daterangepicker是bootstrap框架后期的一个时间控件: 可以设定多个时间段选项:也可以自定义时间段:由用户自己选择起始时间和终止时间:时间段的最大跨度可以 ...
- Delphi2007中正确调用SetWindowLong隐藏程序任务栏图标
http://terony.blog.sohu.com/71347192.html Delphi2007中正确调用SetWindowLong隐藏程序任务栏图标 标签: Delphi2007 SetW ...
- 尚学python课程---15、python进阶语法
尚学python课程---15.python进阶语法 一.总结 一句话总结: python使用东西要引入库,比如 json 1.python如何创建类? class ClassName: :以冒号结尾 ...
- scrapy-redis分布式爬取tencent社招信息
scrapy-redis分布式爬取tencent社招信息 什么是scrapy-redis 目标任务 安装爬虫 创建爬虫 编写 items.py 编写 spiders/tencent.py 编写 pip ...
- 01、requests 基本使用
requests模块的基本使用 基于网络请求的模块. 环境的安装:pip install requests 作用:模拟浏览器发起请求 分析requests的编码流程: 1.指定url 2.发起了请求 ...
- Future Parttern 先给你这张提货单
Future是未来,预期的意思,Thread-permessage模式是指将任务交给其他线程来做,但是如果想知道处理的结果,就要使用Future模式,它的典型应用时执行一个需要花一些时间的方法,会立即 ...
- Unity开发一些实用的提高效率的技巧
该文章参考总结自Unity微信官方 原文: Unity小技巧介绍 1 如果编辑器意外崩溃了,但场景未保存,这时可以打开工程目录,找到/Temp/_Backupscenes/文件夹,可以看到有后缀名为. ...