Java泛型原理笔记

<T> T 到底是什么东东

Java泛型的语法相当的别扭,看到一个这样的写法,感觉到很神奇,正好研究下Java泛型是怎么实现的。

public class A{
	public static void main(String[] args) {
		A a = new A();
		a.test();

		String r = a.test();
	}

	public <T> T test() {
		return (T) new Object();
	}
}

刚开始时,我看到那个"<T> T“ 感觉很神奇,但没什么意义。

查看下test()函数生成的字节码:

public test()Ljava/lang/Object;
   L0
    LINENUMBER 14 L0
    NEW java/lang/Object
    DUP
    INVOKESPECIAL java/lang/Object.<init>()V
    ARETURN

可以发现,这个函数实际上的返回类型是Object,和T没什么关系。

再看下调用test()函数的地方对应的字节码:

		a.test();

		String r = a.test();

字节码:

   L1
    LINENUMBER 5 L1
    ALOAD 1
    INVOKEVIRTUAL A.test()Ljava/lang/Object;
    POP
   L2
    LINENUMBER 7 L2
    ALOAD 1
    INVOKEVIRTUAL A.test()Ljava/lang/Object;
    CHECKCAST java/lang/String
    ASTORE 2

可以看到a.test() 实际上只是调用了下test()函数,返回值直接被pop掉了,没有那个T什么事。

String r = a.test()处,则有个CHECKCAST指令,检查类型转换有没有成功。

所以我们可以看到<T> T这种写法实际上是一个语法糖,它和下面这种写法从本质上来说没有区别。

public class A{
	public static void main(String[] args) {
		A a = new A();
		a.test();

		String r = (String) a.test();
	}
	public Object test() {
		return new Object();
	}
}

extends的情况

下面再来看个复杂点的例子:

public class A{
	interface interface1{
		public String interfaceOne ();
	}
	public <T extends Date & interface1> T test1(T t) {
		t.interfaceOne();
		t.toLocaleString();
		return null;
	}
}

对应的字节码分析:

  public test1(Date) : Date
   L0
    LINENUMBER 21 L0
    ALOAD 1: t
    CHECKCAST A$interface1
    INVOKEINTERFACE A$interface1.interfaceOne() : String
    POP
   L1
    LINENUMBER 22 L1
    ALOAD 1: t
    INVOKEVIRTUAL Date.toLocaleString() : String
    POP
   L2

可以看到,用了extends来限定参数的类型后,函数传进来的参数直接是Date类型的了。

不过,当中间调用到interface1.interfaceOne()时,还是需要一个CHECKCAST来进行类型转换。

关于CHECKCAST指令

http://www.vmth.ucdavis.edu/incoming/Jasmin/ref--7.html

checkcast checks that the top item on the operand stack (a reference to an object or array) can be cast to a given type. For example, if you write in Java:


return ((String)obj);

then the Java compiler will generate something like:

aload_1                        ; push -obj- onto the stack
checkcast java/lang/String     ; check its a String
areturn                        ; return it

checkcast is actually a shortand for writing Java code like:

if (! (obj == null  ||  obj instanceof <class>)) {
    throw new ClassCastException();
}
// if this point is reached, then object is either null, or an instance of
// <class> or one of its superclasses.

所以CHECKCAST指令实际上和INSTANCEOF指令是很像的,不同的是CHECKCAST如果失败,会抛出一个异常,INSTANCEOF是返回一个值。

http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.instanceof

The instanceof instruction is very similar to the checkcast instruction
(§checkcast).
It differs in its treatment of null, its behavior when its test fails (checkcast throws
an exception,instanceof pushes a result code), and its effect on the operand stack.

另外,据虚拟机专家RednaxelaFX的说法,JVM有可能在运行时优化掉一些CHECKCAST指令。

http://hllvm.group.iteye.com/group/topic/25910

总结:

JAVA的泛型只是一个语法糖,实际上在运行时还是有类型转换的过程,从JVM生成的代码来看,和传递一个Object(或者extends的类型)没什么区别。当然泛型的最大好处是编绎期的类型错误检查。

明白JAVA泛型的大致实现原理之后,看很多泛型代码都比较清晰了:)

和C++的泛型比较,C++的泛型是在编绎期实现的,为每一个类型都生成一份代码,所以C++的泛型容易让编绎后的代码出现膨胀。

C++不会保证在运行时,你硬塞一个什么东东进去函数里去执行的结果(极有可能程序挂掉了)。

但是Java代码是跑在JVM里的,要保证程序无论如何都能正常跑,所以泛型肯定会有CHECKCAST这样的消耗。

实际上CHECKCAST算是一个运行时的输入检查了,而C++没有这种检查,Java则要求要这种检查。而JVM则有可能优化掉这些检查,比如前面已经确认过对象的类型了,那么CHECKCAST就有可能被优化掉。

时间: 2025-01-30 07:04:41

Java泛型原理笔记的相关文章

java 泛型的类型擦除与桥方法

java 泛型 学习java泛型的笔记,详细写明白在学习泛型的过程中的笔记心得等: 泛型类 泛型方法 类型擦除(Type Erasure) 桥方法 泛型类 代码参考:java核心技术 卷1 第十版 public class Pair<T> { private T first; private T second; //构造器 public Pair() { first = null; second = null;} public Pair(T first , T second ) {this.f

java泛型的学习笔记[2]—实际使用

继上一文<java泛型的学习笔记[1]-基础知识>之后,本文将介绍泛型的一些应用和应用过程中遇到的问题. 在此之前,我们先给出一张类图:   1)泛型类型的子类型问题 我们首先来看这样一句代码.该行代码正确,因为Cat是Animal的子类型 Animal animal=new Cat();// 但是再看下一句代码: AarrayList<Animal> animals=new ArrayList<Cat>();//编译出错        这句代码编译出错,因为虽然因为C

java泛型(二)、泛型的内部原理:类型擦除以及类型擦除带来的问题

参考:java核心技术 一.Java泛型的实现方法:类型擦除 前面已经说了,Java的泛型是伪泛型.为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉.正确理解泛型概念的首要前提是理解类型擦出(type erasure). Java中的泛型基本上都是在编译器这个层次来实现的.在生成的Java字节码中是不包含泛型中的类型信息的.使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉.这个过程就称为类型擦除. 如在代码中定义的List<object>和List<

java 泛型擦除发生在哪个阶段,如何用反编译工具查看泛型擦除后的代码?

问题描述 java 泛型擦除发生在哪个阶段,如何用反编译工具查看泛型擦除后的代码? 有如下的泛型类: public class Pair { public Pair() { first = null; second = null; } public Pair(T first, T second) { this.first = first; this.second = second; } public T getFirst() { return first; } public T getSecon

Java泛型简明教程

本文是从 Java Generics Quick Tutorial 这篇文章翻译而来. 泛型是Java SE 5.0中引入的一项特征,自从这项语言特征出现多年来,我相信,几乎所有的Java程序员不仅听说过,而且使用过它.关于Java泛型的教程,免费的,不免费的,有很多.我遇到的最好的教材有: The Java Tutorial Java Generics and Collections, by Maurice Naftalin and Philip Wadler Effective Java中文

java泛型学习(2)

一:深入泛型使用.主要是父类和子类存在泛型的demo /** * 父类为泛型类 * @author 尚晓飞 * @date 2014-7-15 下午7:31:25 * * * 父类和子类的泛型. * [泛型的具体声明] * (1)子类直接声明具体类型 * (2)使用时指定具体类型(new 对象时) * (3)子类泛型>=父类泛型(个数,类型,顺序无关) * * [泛型的确定] * (1)属性:在父类中,泛型随父类泛型而定 * 子类中,泛型随子类泛型而定 * (2)重写方法中: * 泛型全部随父类

java泛型(一)、泛型的基本介绍和使用

现在开始深入学习java的泛型了,以前一直只是在集合中简单的使用泛型,根本就不明白泛型的原理和作用.泛型在java中,是一个十分重要的特性,所以要好好的研究下. 泛 型的定义:泛型是JDK 1.5的一项新特性,它的本质是参数化类型(Parameterized Type)的应用,也就是说所操作的数据类型被指定为一个参数,在用到的时候在指定具体的类型.这种参数类型可以用在类.接口和方法的创建中,分别称为泛 型类.泛型接口和泛型方法.   泛型思想早在C++语言的模板(Templates)中就开始生根

java泛型type体系整理

一直对jdk的ref使用比较模糊,早上花了点时间简单的整理了下,也帮助自己理解一下泛型的一些处理.   java中class,method,field的继承体系     java中所有对象的类型定义类Type   说明:    Type :  Type is the common superinterface for all types in the Java programming language. These include raw types, parameterized types, 

Java Network Programming 笔记(1)

笔记   Java Network Programming 笔记 n5 一 网络基本概念Chapter2 Basic Network Concepts 2.1 Networkskeywords: network, node, host, address, name, packet-switched, protocol 网络是可以或多或少实时地相互发送和接收数据的计算机和其他设备的集合. 网络上的每台机器被称作结点(node),大多数结点是计算机,但是打印机,路由器,桥,网关,哑终端和可口可乐机都