====================================================================================================
今天深入学习了下java泛型。看了coreJAVA中的泛型部分,然后网上看了些资料,发现这篇博客写的很好,表达很清楚。摘抄如下
原文链接: http://blog.sina.com.cn/s/blog_44c1e6da0100coxb.html
Java泛型的本质是什么哪?虚拟机是如何对泛型进行处理的的那?
private Tfirst;
private Tsecond;
publicPair(T first, T second){
this.first = first;
this.second = second;
}
public voidsetFirst(T first){
}
public TgetFirst(){
return first;
}
public voidsetSecond(T second){
}
// public void setSecond(Objectsecond){
// this.second = (T) second;
// }
public TgetSecond(){
return second;
}
}
//域
privatejava.lang.Object first;
privatejava.lang.Object second;
//构造器
publicPair(java.lang.Object, java.lang.Object);
//方法
public voidsetFirst(java.lang.Object);
public voidsetSecond(java.lang.Object);
publicjava.lang.Object getSecond( );
publicjava.lang.Object getFirst( );
}
如果将泛型类Pair的类型参数加上限定,比如Pair<T extendsComparable>,再使用类分析器对其进行分析,结果:
//域
privatejava.lang.Comparable first;
privatejava.lang.Comparable second;
//构造器
publicPair(java.lang.Comparable,java.lang.Comparable);
//方法
public voidsetFirst(java.lang.Comparable);
public voidsetSecond(java.lang.Comparable);
publicjava.lang.Comparable getSecond( );
publicjava.lang.Comparable getFirst( );
}
使用类型参数的限定进行了替换,这与预计的相同。
- 对原始方法getFirst()的调用;
- 将返回的Object对象强制转换成GregorianCalendar。
public class ArrayAlg{
publicstatic <T> T getMiddle(T[]t){
System.out.println("泛型方法");
return t[t.length/2];
}
// public static Object getMiddle(Object[]o){
// return o[o.length/2];
// }
publicstatic <T extends Comparable> Tmin(T[] a){
if(a == null || a.length == 0){
return null;
}
T smallest = a[0];
for(int i = 1;i <a.length;i++){
if(smallest.compareTo(a[i]) >0){
smallest = a[i];
}
}
return smallest;
}
publicstatic <T extends Comparable>Pair<T> minmax(T[]ts){
if(ts == null || ts.length == 0){
return null;
}
T min = ts[0];
T max = ts[0];
for(int i = 0;i <ts.length;i++){
if(min.compareTo(ts[i]) >0){
min = ts[i];
}
if(max.compareTo(ts[i]) <0){
max = ts[i];
}
}
return new Pair<T>(min,max);
}
// public staticPair<Comparable> minmax(Comparable[]ca){
// return null;
// }
publicstatic void main(String[] args) {
String[] s = {"AAA","BBB","CCC"};
System.out.println(ArrayAlg.<String>getMiddle(s));//在方法名前指定类型
// System.out.println(<String>getMiddle(s));//不能这样用,虽然调用的是处在同一个类中静态方法,语法问题,<>不能加在方法名前
Date[] d = {new Date(),new Date(),newDate()};
System.out.println(getMiddle(d));//其实可以不指定参数,编译器有足够的信息推断出要调用的方法
int[] is = {100,200,300};
System.out.println(getMiddle(is));
}
}
使用类分析器对其进行分析,结果:
public class ArrayAlg extendsjava.lang.Object{
//方法
publicstatic int getMiddle(int[]);
publicstatic java.lang.ObjectgetMiddle(java.lang.Object[]);
publicstatic Pair minmax(java.lang.Comparable[]);
publicstatic void main(java.lang.String[]);
publicstatic java.lang.Comparablemin(java.lang.Comparable[]);
}
泛型方法的类型擦除会带来两个问题1.类型擦除与多态的冲突;2.方法签名冲突。
我们来看一个结构相对繁杂一些的类,类DateInterval继承前面定义的泛型类Pair<T>:
public class DateInterval extendsPair<Date> {
publicDateInterval(Date first, Date second){
super(first, second);
}
@Override
public voidsetSecond(Date second) {
super.setSecond(second);
}
@Override
public DategetSecond(){
return super.getSecond();
}
publicstatic void main(String[] args) {
DateIntervalinterval = new DateInterval(new Date(), newDate());
Pair<Date> pair =interval;//超类,多态
Date date = new Date(2000, 1, 1);
System.out.println("原来的日期:"+pair.getSecond());
System.out.println("set进新日期:"+date);
pair.setSecond(date);
System.out.println("执行pair.setSecond(date)后的日期:"+pair.getSecond());
}
}
我们知道Java中的方法调用采用的是动态绑定的方式,应该呈现出多态的特性。子类覆写超类中的方法,如果将子类向下转型成超类后,仍然可以调用覆写后的方法。但是泛型类的类型擦除造成了一个问题,Pair的原始类型中存在方法
public void setSecond(Objectsecond);
DateInterval中的方法
public void setSecond(Datesecond);
我们的本意是想覆写Pair中的setSecond方法,但是从方法签名上看,这完全是两个不同的方法,类型擦除与多态产生了冲突。而实际情况那?运行DateInterval的main方法,我们看到
public void setSecond(Datesecond)的确覆写了public void setSecond(Object second)方法。这是如何做到的那?
使用Java类分析器对其进行分析,结果:
public class DateInterval extendsPair{
//构造器
publicDateInterval(java.util.Date,java.util.Date);
//方法
public voidsetSecond(java.util.Date);
public volatile voidsetSecond(java.lang.Object);//方法1
public java.util.DategetSecond( );//方法2
public volatilejava.lang.Object getSecond();//方法3,它难道不会和方法1冲突?
publicstatic void main(java.lang.String[]);
}
方法1和方法3是我们在源码中不曾定义的,它肯定是由编译器生成的。这个方法称为桥方法(bridgemethod),真正覆写超类方法的是它。语句pair.setSecond(date)实际上调用的是方法1,publicvolatile void setSecond(Object),通过这个方法再去调用public voidsetSecond(Date)。这个桥方法的实际内容是:
public void setSecond(Objectsecond){
this.setSecond((java.util.Date) second );
}
这样的结果就符合面向对象中多态的特性了,实现了方法的动态绑定。但是,这样的做法给我们带来了一种错觉,就认为public voidsetSecond(Date)覆写了泛型类的public voidsetSecond(Object),如果我们在DateInterval中增加一个方法:
publicvoid setSecond(Object obj){
System.out.println("覆写超类方法!");
}
public ListgetList(){
return null;
}
}
@Override
publicArrayList getList(){
return null;
}
}
//域
//构造器
public ASub();
//方法
publicjava.util.ArrayList getList( );
publicvolatile java.util.List getList( );
}
相关推荐
Java 泛型、原始类型、桥接方法 Java 泛型是 Java 语言中的一种重要特性,用于在编译时检查类型安全性,避免 ClassCastException 的出现。下面将详细介绍 Java 泛型、原始类型和桥接方法。 Java 泛型 Java 泛型...
总结来说,Java泛型的类型擦除虽然在运行时消除了类型信息,但通过编译时的类型检查、桥接方法、通配符等补偿机制,仍然实现了强大的类型安全和便利性。开发者应理解这些补偿机制,以便更好地利用Java泛型进行类型...
10. **反射和泛型**:虽然泛型信息在运行时被擦除,但通过反射可以获取到泛型的原始类型信息,这对于编写一些高级的库和工具非常有用。 通过理解和熟练运用这些知识点,开发者可以编写出更加健壮、可读性更强的Java...
9. **Erasure和桥接方法**:由于类型擦除,有时需要生成桥接方法来保持方法签名的兼容性。 理解并熟练运用这些概念,有助于编写更安全、更易于维护的代码。泛型是Java编程中的重要工具,能帮助我们避免类型转换错误...
然而,如果直接将泛型实例转换为其原始类型,可能会产生“rawtypes”,导致野指针异常。 6. **创建泛型数组的限制** 由于类型擦除,无法直接创建泛型类型的数组,如`new Box[10]`是非法的。但可以创建Object类型的...
6. **桥接方法**:由于类型擦除,有时编译器会自动生成桥接方法以保持方法签名的一致性。 7. **基本类型与泛型**:Java的泛型不支持基本类型,需要使用包装类,如`Integer`代替`int`。 8. **泛型类型的实例化**:...
- **桥接方法**:解释编译器如何生成额外的方法来处理泛型类型和原始类型的交互。 - **类型推断**:编译器如何自动推断出泛型参数的类型。 #### 结论 Java泛型是Java编程语言的重要组成部分,它提供了强大的工具来...
**类型擦除**是Java泛型的关键特性,意味着在编译后,泛型类型信息会被删除,只保留原始类型。这一机制使得Java的泛型与C++的模板机制有所区别,并导致了一些特殊的规则和限制: - **没有独立的泛型类Class对象**:...
了解各种数据类型,包括原始类型和引用类型。 - **类与对象**:掌握面向对象编程的基本概念,如封装、继承、多态。理解类、对象、构造器、方法等概念。 - **异常处理**:理解异常的分类,如何抛出和捕获异常,以及...
这份文档将重点放在Java的基本数据类型和相关的面试题目上,如原始类型(int、char、boolean等)、自动装箱拆箱、数值溢出、运算符优先级、类型转换等方面,是准备基础面试的重要参考资料。 通过这些资源的学习,...
1. **基础语法**:包括变量、数据类型(原始类型与引用类型)、运算符、流程控制(条件语句、循环语句)、方法定义与调用等。 2. **面向对象**:封装、继承、多态是面向对象的三大特性。理解类的构造器、访问修饰符...
- 数据类型:原始类型与引用类型的区别,自动装箱与拆箱。 - 流程控制:循环、条件语句、异常处理。 - 集合框架:List、Set、Map接口及其实现类的特性与区别。 - 多线程:线程的创建方式,同步机制...
1. **基础语法**:面试通常会从Java的基础语法开始,包括数据类型(原始类型与引用类型)、运算符、流程控制(if, switch, for, while等)、异常处理、类与对象、封装、继承、多态等概念。 2. **集合框架**:面试中...
- **泛型**:泛型用于在编译时检查类型安全,减少类型转换错误。 7. **多线程** - **Thread类与Runnable接口**:创建线程的两种方式,实现Thread类或实现Runnable接口并传入Thread。 - **同步机制**:...