Java编程思想:擦除的神秘之处
import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { // ErasureAndInheritance.test(); // ArrayMaker.test(); FilledListMaker.test(); } } /* 15.7 擦除的神秘之处 Class.getTypeParameters()将返回一个TypeVariable对象数组,表示有 泛型声明所声明的类型参数。我之前确实有过这种需求,但是这个方法不可能给 我想要的结果 问题: 因此,你可以知道诸如类型参数标识符和泛型类型边界这类的信息——你却无法知道用来 创建某个特定实例的实际的类型参数。Java泛型是使用擦除来实现的,这意味着当你在 使用泛型的时候,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象 因此List<String>和List<Integer>在运行时事实上是相同的类型。这两种类型都被 擦除成了它们的“原生”类型,即List。理解擦除以及应该如何处理它,是你在学习Java 泛型时面临的最大障碍。 ——既然被擦除了,为什么我们还能取到我们想要的类型 ——C++中如果你用了泛型,编译器就会对应的生成这个类,这一点还是蛮 舒服的。 */ /* 15.7.1 C++的方式 C++: template<class T> class Manipulator { T obj; public: Manipulator(T x) { obj = x;} void manipulator() { obj.f();} } Java: 无法通过编译 class Manipulator<T> { T obj; public Manipulator(T x) { obj = x; } void manipulator() { obj.f();} } 解决问题: 为了调用f(),我们必须协助泛型类,给定泛型类的边界,以此告知 编译器只能接受这个边界的类型。这里重用了extends关键字 <T extends HasF> : 声明T必须具有类型HasF或者从HasF导出的类型, 我们说泛型类型参数将擦除到它的第一个边界(可能会有多个边界)。 */ class HasF { public void f() {} } class Manipulator<T extends HasF> { T obj; public Manipulator(T x) { obj = x; } void manipulator() { obj.f();} } /* 在我们的案例中,泛型没有任何贡献。我们完全可以自己执行泛型擦除。这一点很重要: 只有当你希望使用的类型参数比某个具体类型(以及它的所有子类型)更加“泛化”时—— 也就是说,当你希望代码能够跨多个类工作时,使用泛型才有所帮助。 ——额,为什么不用接口呢?接口不也是这种功能么,甚至比这个还有强大一点 因此,类型参数和它们在有用的泛型代码中的应用,通常比简单的类替换要更复杂。但是, 不能因此而认为<T extends HasF>形式的任何东西都是有缺陷的。例如,如果某个类有 一个返回T的方法,那么泛型就有所帮助,因此它们之后将返回确切的类型。 ——我想知道,既然类型已经被擦除了,那返回时是如何返回成T的了, 难道是编译器私下里进行了一次强制转换? */ /* 15.7.3 擦除的问题 擦除的主要的正当理由是非泛型的代码到泛型到泛型代码的转变过程。擦除的代价是 显著的。泛型不能用于显式的引用运行时类型的操作之中,例如转型、instanceof 操作和new表达式。因为所有关于参数的类型的信息都丢失了。 */ class GenericBase<T>{ private T element; public void set(T arg) { element = arg;} public T get(){return element;} } class Dervied1<T> extends GenericBase<T> {} class Dervied2 extends GenericBase {} class ErasureAndInheritance{ static void test() { Dervied2 d = new Dervied2(); Object obj = d.get(); d.set(obj); } } /* 17.5.4 边界处的动作 */ /* 编译器不会给出任何警告,尽管我们从擦除中知道在create()内部的new ArrayList<T> 中的<T>被移除了——在运行时,这个类的内部没有任何的<T>。但是,即使编译无法知道有 关create()中的T的任何信息,但是它仍旧可以在编译期间确保你放置到result中的对象 具有T类型,使其适合ArrayList<T>。 */ class ArrayMaker<T>{ //运行时实际保存的是Class<Object> private Class<T> kind; public ArrayMaker(Class<T> kind) { //这个地方在运行时,把一个Class<T>赋给了Class<Object> this.kind=kind; } @SuppressWarnings("unckecked") T[] create(int size) { return (T[]) Array.newInstance(kind,size); } public static void test() { ArrayMaker<String> s = new ArrayMaker<>(String.class); String[] stringArray = s.create(); // System.out.println(Arrays.toString(stringArray)); } } class ListMaker<T>{ List<T> create(){return new ArrayList<T>();} public static void test() { ListMaker<String> s = new ListMaker<>(); List<String> strs = s.create(); } } class FilledListMaker<T> { List<T> create(T t, int n) { List<T> result = new ArrayList<T>(); ; i < n; i++) { result.add(t); } return result; } public static void test() { FilledListMaker<String> stringMaker = new FilledListMaker<>(); List<String> list = stringMaker.create(); System.out.println(list); } } /* 因为擦除在方法体中移除了类型信息,所以在运行时的问题就是边界:即对象进入和 离开方法的地点。这些正是编译器在编译期执行类型检查并插入转型代码的地点。 */
Java编程思想:擦除的神秘之处的更多相关文章
- Java编程思想(11~17)
[注:此博客旨在从<Java编程思想>这本书的目录结构上来检验自己的Java基础知识,只为笔记之用] 第十一章 持有对象 11.1 泛型和类型安全的容器>eg: List<St ...
- Java中的泛型 --- Java 编程思想
前言 我一直都认为泛型是程序语言设计中一个非常基础,重要的概念,Java 中的泛型到底是怎么样的,为什么会有泛型,泛型怎么发展出来的.通透理解泛型是学好基础里面中非常重要的.于是,我对<Ja ...
- 《Java编程思想》读书笔记(二)
三年之前就买了<Java编程思想>这本书,但是到现在为止都还没有好好看过这本书,这次希望能够坚持通读完整本书并整理好自己的读书笔记,上一篇文章是记录的第一章到第十章的内容,这一次记录的是第 ...
- 《Java编程思想》读书笔记(四)
前言:三年之前就买了<Java编程思想>这本书,但是到现在为止都还没有好好看过这本书,这次希望能够坚持通读完整本书并整理好自己的读书笔记,上一篇文章是记录的第十七章到第十八章的内容,这一次 ...
- Java 中的泛型详解-Java编程思想
Java中的泛型参考了C++的模板,Java的界限是Java泛型的局限. 2.简单泛型 促成泛型出现最引人注目的一个原因就是为了创造容器类. 首先看一个只能持有单个对象的类,这个类可以明确指定其持有的 ...
- JAVA编程思想——分析阅读
需要源码.JDK1.6 .编码风格参考阿里java规约 7/12开始 有点意识到自己喜欢理论大而泛的模糊知识的学习,而不喜欢实践和细节的打磨,是因为粗心浮躁导致的么? cron表达式使用 设计能力.领 ...
- JAVA编程思想(第四版)学习笔记----4.8 switch(知识点已更新)
switch语句和if-else语句不同,switch语句可以有多个可能的执行路径.在第四版java编程思想介绍switch语句的语法格式时写到: switch (integral-selector) ...
- MyEclipse导入ant项目——Java编程思想
北门煎饼东门串儿: <JAVA编程思想(Think in Java)>一书中提供了大量源代码,可是项目是用ant构建的.对于用惯了eclipse,netbeans等IDE的同学们可能有些手 ...
- Java编程思想第四版勘误
坊间传说这本书翻译得很烂,我倒觉得还好.虽然看原文更准确,但是如果在具备一定编程思维和基础.能够看出来疑问的情况下,还是看中文更快一些,而且这本书本身也不适合初学者看.当然,错误和不通顺还是有的,而且 ...
随机推荐
- VS中添加第三方库及相对路径设置
原文 VS中添加第三方库及相对路径设置 对于一些第三方的SDK,一般会包含头文件(*.h),静态库文件(*.lib)和动态库文件(*.dll). 1. 文件位置:为了提高程序的可移植性,将第三库放在 ...
- SVN更新报错问题(Please execute the 'Cleanup' command)
SVN更新报错问题(Please execute the 'Cleanup' command) https://segmentfault.com/a/1190000012571289 svn: E20 ...
- 用vs2010编译好的ICU库
1.ICU库的官网网址为http://site.icu-project.org/ 2.ICU(International Components for Unicode)是一个国际化的字符编码和转化的库 ...
- c# RedisHelper
使用redis组件如下,至于为什么使用3.9版本,是因为4.0开始商业了,限制了次数 ServiceStack.Common" version="3.9.70"Servi ...
- SQLite实现内存键值存储
SQLite数据文件往Linux内存文件系统/dev/shm/data.sqlite3一放,就是内存级读写性能的SQL系统.用SQLite实现内存键值存储:CREATE TABLE IF NOT EX ...
- Dependency Injection 筆記 (3)
续上集.接着要来进一步了解的是 DI 的实现技术,也就是注入相依对象的方式.这里介绍的依赖注入方式,又称为「穷人的 DI」(poor man’s DI),因为这些用法都与特定 DI 工具无关,亦即不使 ...
- 仿写confirm和alert弹框
在工作中,我们常常会遇到原生的样式感觉比较丑,又和我们做的项目风格不搭.于是就有了仿写原生一些组件的念头,今天我就带大家仿写一下confirm和alert样式都可以自己修改. 有些的不好的地方请指出来 ...
- JavaWEB路径总结
这篇文章是小编一直想写的一篇,主要是对web阶段中各个路径进行的一些总结,希望读者看过之后对于路径方面有一个清晰的认识.首先声明一点:世界上一切东西都是相对的,对于这点而言,相信大家并不陌生,从初中开 ...
- Python 爬虫从入门到进阶之路(八)
在之前的文章中我们介绍了一下 requests 模块,今天我们再来看一下 Python 爬虫中的正则表达的使用和 re 模块. 实际上爬虫一共就四个主要步骤: 明确目标 (要知道你准备在哪个范围或者网 ...
- scikit-learn学习笔记-bili莫烦
bilibili莫烦scikit-learn视频学习笔记 1.使用KNN对iris数据分类 from sklearn import datasets from sklearn.model_select ...