Java擦除

概述:

   Java泛型在使用过程有诸多的问题,如不存在List<String>.class, List<Integer>不能赋值给List<Number>(不可协变),奇怪的ClassCastException等。 正确的使用Java泛型需要深入的了解Java的一些概念,如协变,桥接方法,以及这篇笔记记录的类型擦除。Java泛型的处理几乎都在编译器中进行,编译器生成的bytecode是不包涵泛型信息的,泛型类型信息将在编译处理是被擦除,这个过程即类型擦除。

编译器如何处理泛型:

通常情况下,一个编译器处理泛型有两种方式:
     1.Code specialization
。在实例化一个泛型类或泛型方法时都产生一份新的目标代码(字节码or二进制代码)。例如,针对一个泛型list,可能需要 针对stringintegerfloat产生三份目标代码。
     2.Code sharing
。对每个泛型类只生成唯一的一份目标代码;该泛型类的所有实例都映射到这份目标代码上,在需要的时候执行类型检查和类型转换。
     C++
中的模板(template)是典型的Code specialization实现。C++编译器会为每一个泛型类实例生成一份执行代码。执行代码中integer liststring list是两种不同的类型。这样会导致代码膨胀(code bloat),不过有经验的C++程序员可以有技巧的避免代码膨胀。
     Code specialization
另外一个弊端是在引用类型系统中,浪费空间,因为引用类型集合中元素本质上都是一个指针。没必要为每个类型都产生一份执行代码。而这也是Java编译器中采用Code sharing方式处理泛型的主要原因。
     Java
编译器通过Code sharing方式为每个泛型类型创建唯一的字节码表示,并且将该泛型类型的实例都映射到这个唯一的字节码表示上。将多种泛型类形实例映射到唯一的字节码表示是通过类型擦除(type erasue)实现的。

   类型擦除指的是通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上。类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且再必要的时候添加类型检查和类型转换的方法。
     
类型擦除可以简单的理解为将泛型java代码转换为普通java代码,只不过编译器更直接点,将泛型java代码直接转换成普通java字节码。
     
类型擦除的主要过程如下:
     1.
将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。
     2.
移除所有的类型参数。

 

擦除使我们在泛型代码内部,无法获得任何有关参数类型的信息。很蛋疼...

例如:

C++中我们可以这样写:

template<typename T> T imax(T a, T b) {T copy;  return copy;
}
class A{};

  

但是在Java中我们是不能够生成copy的因为们压根就不知道T的类型信息。

 

那为什么Java要使用擦除呢?

首先能够节省空间避免代码膨胀,主要原因是为了“迁移兼容性”,即允许泛型代码与非泛型代码共存,因为泛型是Java后期才添加的为了兼容以前的代码所以采取了折中的办法。

那么擦除所带来的问题我们如何解决呢?

1: 通过引入类型标签来对擦除进行补偿:

class Building{@Overridepublic String toString() {return "Building ...";}
}
class House extends Building{@Overridepublic String toString() {return "House ...";}
}class TestItem<T>{Class<T> type;  //通过添加类型标签,来获得我要持有的类型的信息public TestItem(Class<T> type) {this.type = type;}public T getInstance() throws InstantiationException, IllegalAccessException {T copy = type.newInstance(); //这样我就可以利用类型信息进行必要的处理了return copy;}
}public class Test {public static void main(String[] args) throws InstantiationException, IllegalAccessException {TestItem<Building> item = new TestItem<>(Building.class);System.out.println(item.getInstance());System.out.println("------------------------");TestItem<House> item2 = new TestItem<>(House.class);System.out.println(item2.getInstance());//出错,因为我们是利用newInstance来创建对象的,就必须保证我们的对象要有默认的构造方法才行,但是Integer没有
//		System.out.println("------------------------");
//		TestItem<Integer> item3 = new TestItem<>(Integer.class);
//		System.out.println(item3.getInstance());}
}

上面的Integer的问题,我们可以通过传入一个工厂来实现

interface Factory<T>{T create();
}
class IntegerFactory implements Factory<Integer> {@Overridepublic Integer create() {return new Integer(0);}
}class TestItem<T>{Class<T> type;  //通过添加类型标签,来获得我要持有的类型的信息T copy;Factory<T> factory;public <F extends Factory<T>>TestItem(F factory) {this.factory = factory;}public TestItem(Class<T> type) {this.type = type;}public T getInstance() throws InstantiationException, IllegalAccessException {//copy = type.newInstance(); //这样我就可以利用类型信息进行必要的处理了copy = factory.create();return copy;}
}public class Test {public static void main(String[] args) throws InstantiationException, IllegalAccessException {
//		TestItem<Building> item = new TestItem<>(Building.class);
//		System.out.println(item.getInstance());
//		System.out.println("------------------------");
//		TestItem<House> item2 = new TestItem<>(House.class);
//		System.out.println(item2.getInstance());//现在把工厂放进去就可以了。System.out.println("------------------------");TestItem<Integer> item3 = new TestItem<>(new IntegerFactory());  System.out.println(item3.getInstance());}
}

  

2: 同样我们可以通过使用设置擦相互边界来补偿擦除

就像我们在前一篇的比较的时候我们将擦除边界设定成了Comparable,保证了我们的类新信息是可比较的。

http://www.cnblogs.com/E-star/p/3438226.html

 

注意:

1.虚拟机中没有泛型,只有普通类和普通方法
2.
所有泛型类的类型参数在编译时都会被擦除
3.
创建泛型对象时请指明类型,让编译器尽早的做参数检查(Effective Java,第23条:请不要在新代码中使用原生态类型)
4.
不要忽略编译器的警告信息,那意味着潜在的ClassCastException等着你。

 

 

转载于:https://www.cnblogs.com/E-star/p/3438290.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/360236.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql数据库相互备份_MySQL的本地备份和双机相互备份脚本

先修改脚本进行必要的配置,然后以root用户执行.1. 第一执行远程备份时先用 first参数.2. 本地备份用local参数3. 远程备份不用参数注意:需要在另一主机上的Mysql用户用添加用户..需要配置的地方:# define host and mysql passwordREMOTE_HOST"" #远程主机名或IPREMOT…

Spring Boot和Swagger UI

我已经一年没有从头开始开发Spring Web应用程序了&#xff0c;如果我不参加QA自动化工程师的培训&#xff0c;那么这段时间甚至会更长。 由于这个原因&#xff0c;我开发了一个示例REST应用程序。 除了Swagger&#xff0c;一切对我来说都很熟悉。 因此&#xff0c;我将描述我在…

mysql5.7.22打不开_windows下mysql-5.7.22-winx64突然启动不了,报错Could not open log file

本文摘自classinstance.cn。windows下mysql-5.7.22-winx64突然启动不了&#xff0c;感觉启动几秒钟后就自己关闭了&#xff0c;看了下启动日志&#xff1a;2019-08-25T10:57:08.389404Z 0 [Warning] option wait_timeout: unsigned value 31536000 adjusted to 21474832019-08-…

HDU1530 最大流问题

第一次写Dinic 然后贴一下 最基础的网络流问题 嘎嘎: #include <iostream> #include<cstdio> #include<string.h> #include<queue> using namespace std; const int M205; __int64 map[M][M]; int n,m,dist[M]; queue<int>q; void readdate() {_…

把python37添加到环境变量配置_关于在win 10上成功创建一个Django项目时遇到django-admin的手动配置环境变量问题。...

前言初学Python Web 在创建第一个Djang项目的时候出现了很多的问题&#xff0c;今天和大家分享并记录一下这次艰难的历程&#xff01;一、官网下载Python以及安装Django1、Python的下载安装链接&#xff1a;大家最好使用谷歌浏览器&#xff0c;因为翻译的很到位(大家下载最新版…

在Ant中显示路径

在博客文章Java and Ant Properties Refresher和Ant <echoproperties /> Task中 &#xff0c;我写了一篇关于如何了解Ant构建如何看到属性的文章&#xff0c;这有助于更好地理解构建。 通常情况下&#xff0c;在构建过程中看到构建中使用的各种路径也很有价值&#xff0c…

如何删除数据库中的所有用户表(表与表之间有外键关系)

1、由表名求字段名 create proc up_008(table varchar(20)) as begin declare sql varchar(99) select sql\select name from syscolumns where idobject_id(\ select sqlsql\\\\table\\\\\)\ --select sql exec(SQL) end exec up_008 a_idx2 2、编程删…

Azure开发者任务之一:解决Azure Storage Emulator初始化失败

初学Windows Azure&#xff1a; 我打算开始学习Windows Azure。我安装了Azure SDK&#xff0c;然后在“Cloud”标签下选择Windows Azure模板&#xff0c;创建了一个项目&#xff0c;然后又创建了一个Web角色。 在aspx文件上&#xff0c;我只添加了一个标签&#xff0c;但是当我…

关于Java里如何跳出一个多重循环

一般我们要跳出一个循环&#xff0c;用break就OK了&#xff0c;比如&#xff1a; 1 for(int i1;i<5;i){ 2   if&#xff08;条件&#xff09; 3     break&#xff1b; 4   //一些代码 5 } 但是如果这时候&#xff0c;在这一层循环外面还有一层循环的话&#…

CCRC认证对企业的作用?

CCRC认证&#xff08;中国网络安全审查认证&#xff09;是针对网络产品和服务的安全审查制度&#xff0c;它对企业的作用主要体现在以下几个方面&#xff1a; 1. 提升产品安全性 CCRC认证要求企业对其网络产品和服务进行全面的安全审查&#xff0c;确保符合国家网络安全标准和…

java运行python3_python写脚本并用java调用python(三)

1)编写mytest.py完成一个简单加法计算# coding:utf8#def 表示一个方法 adderdef adder(a, b):return ab#这里执行adder方法并打印出结果print adder(1,2)2)运行以上脚本方式如图12 3 打印成功&#xff01;3)java调用python脚本的两种方式Process process Runtime.getRuntime(…

Hibernate教程– ULTIMATE指南(PDF下载)

编者注&#xff1a;在本文中&#xff0c;我们提供了全面的Hibernate教程。 Hibernate ORM&#xff08;简称Hibernate&#xff09;是一个对象关系映射框架&#xff0c;它有助于将面向对象的域模型转换为传统的关系数据库。 Hibernate通过用高级对象处理功能代替直接与持久性相关…

mysql单源多表同步单库单表_MySQL主从复制单表或者多表

MySQL数据库安装不过多的介绍了&#xff1a;必须保证2个数据库的版本一致。 主数据库&#xff1a;192.168.0.43 从数据库&#xff1a;192.168.0.53 修改43主数据MySQL数据库安装不过多的介绍了&#xff1a;必须保证2个数据库的版本一致。主数据库&#xff1a;192.168.0.43从数据…

悲观锁定时如何避免可怕的死锁-以及Java 8的一些用法!

有时您根本无法避免&#xff1a;通过SQL进行悲观锁定。 实际上&#xff0c;当您要在共享的全局锁上同步多个应用程序时&#xff0c;它是一个很棒的工具。 有些人可能认为这是在滥用数据库。 如果可以解决您遇到的问题&#xff0c;我们认为可以使用您拥有的工具。 例如&#xf…

object - c 函数的值

函数名说明int rand()随机数生成。&#xff08;例&#xff09;srand(time(nil)); //随机数初期化int val rand()%50; //0&#xff5e;49之间的随机数int abs(int a)整数的绝对值&#xff08;例&#xff09;int val abs(-8); →8※浮点数的时候用fabs。double fabs(double …

xshell 秘钥连接_如何使用PuTTY和xshell 分别远程连接linux,并配置秘钥认证

使用PuTTY 连接并配置密钥认证第一步&#xff1a;下载PuTTY下载 .zip 64位的电脑 32位的putty也能用第二步&#xff1a;配置基本信息打开 PuTTY端口默认是22 (端口是可以改的)ip 地址如果忘记&#xff0c;ifconfig 查看一下Load >Open输入登录名 密码即可完成登录若出现上…

滨河新区(黄河楼)夜景

转载于:https://www.cnblogs.com/ysx4221/p/3454517.html

使用Junit测试名称

命名测试 在创建Junit测试时&#xff0c;通常没有实际使用该方法的名称。 Junit运行程序使用反射来发现测试方法&#xff0c;并且从版本4开始&#xff0c;您不再被限制以test开始方法的名称。 测试方法的名称用于文档目的。 人们遵循不同的风格。 您可以使用给定的given_Somet…

java 门面模式_Java门面模式

一、简介隐藏系统的复杂性&#xff0c;对外提供统一的访问入口&#xff0c;外部系统访问只通过此暴露出的统一接口访问。是一种结构型模式。封装子系统接口的复杂性&#xff0c;提供统一的对外接口&#xff0c;能够使子系统更加简单的被使用。二、结构及使用场景如上所示&#…

摘成功道路上容易被忽视的5项技能

本文摘自《电子工程师专辑》&#xff0c;来源&#xff1a;http://forum.eet-cn.com/FORUM_POST_10008_1200257568_0.HTM。 人人都渴望成功&#xff0c;在通往成功的道路上&#xff0c;少不了技能和运气&#xff0c;运气往往难以受人控制&#xff0c;技能却能好好把握&#xff0…