美文网首页
2018-06-02 8.5 虚拟机对泛型代码的处理

2018-06-02 8.5 虚拟机对泛型代码的处理

作者: 静者达观 | 来源:发表于2018-06-02 01:14 被阅读0次

以下部分只是将我在学习笔记中的关于Java核心技术卷一 8.5 的笔记单拆出来,形成一篇文章。

8.5 泛型代码和虚拟机

1.无论何时定义一个泛型类型,都自动提供了一个相应的原始类型(raw type)。原始类型的名字就是删去类型参数后的泛型类型名。擦除(erased) 类型变量, 并替换为限定类型(无限定的变量用 Object)。

2.java中的泛型只在程序源码中存在,当编译成字节码之后,就会变为原始类型,java实现泛型的方法是类型擦除。

3.当程序调用泛型方法时,如果擦除返回类型, 编译器插入强制类型转换。例如,下面这个语句序列

Pair buddies = . .

Employee buddy = buddies.getFirst();

擦除

getFirst 的返回类型后将返回 Object 类型。编译器自动插人 Employee

的强制类型转换,将返回的Object类型转换为Employee类型。当存取一个泛型域时也要插入强制类型转换。假设 Pair 类的 first

域和 second 域都是公 有的。表达式:

Employee buddy = buddies.first;

就会在结果字节码中插入强制类型转换。

4.类型擦除也会出现在泛型方法中,由此产生一些问题:在一个子类中,如果对于父类的一个函数进性了重载,则在擦除之后就会破坏重载。比如说:

class Datelnterval extends Pair

{

    public void setSecond(LocalDate second)

    {

        if (second.compareTo(getFirstO) >= 0)

        super.setSecond(second);

    }

}

在被擦除之后,就会变为:

class Datelnterval extends Pair

{

    public void setSecond(LocalDate second)

    {……}

}

而在父类中,则有一个public

void setSecond(Object second)方法,原本这个方法应该作为模板方法被实例化为public void

setSecond(LocalDate

second)而后被子类中的同名方法所重载,但是此时由于擦除,重载被破坏了,父类的方法与子类中的这个方法变为了两个方法,public void

setSecond(Object second)和public void setSecond(LocalDate

second)共同出现在子类中。

因此,编译器的解决方法是在作为子类的Datelnterval类中,生成一个桥方法,即为:

public

void setSecond(Object second) { setSecond((Date) second);

},这个方法使用了与父类方法在擦除之后相同的函数名,返回值,与参数列表,从而完全将父类方法覆盖,而这个桥方法的作用则是,将变量强制类型转换之后,传入子类方法,而在实际使用时,如果一个Pair的引用引用了一个Datelnterval类的变量,在接收参数时,即使调用桥函数,也会和以前一样运行。

5.假设Datelnterval类也重载了getSecond()方法,比如说:

class Datelnterval extends Pair

{

    public LocalDate getSecond()

    {

     return (Date) super.getSecond().clone();

    }

    ……

}

那么,在擦除之后,这个类中就会有两个函数:

public LocalDate getSecond()

public Object getSecond()/*这是一个桥方法,它将Pair类中的方法给覆盖掉了*/

由于虚拟机中,是使用参数类型和返回类型唯一确定一个方法,因此虽然在源码中无法写出如此形式,但是在最后生成的字节码中,却会出现以上这种情况。

6.这也导致了其他问题,比如说:

public class TestTheBug

{

public static void method (Pair pairex)  {        System.out.println("Pair string pairex");    }

public static void method (Pair pairex)  {        System.out.println("Pair int pairex");    }

}

这段代码表面上没有问题,但事实上是无法进行编译的,因为在进性擦除之后,这两个函数的函数头部已经完全一致了,由此造成错误。但是,如果你进行这样的修改:

public class TestTheBug

{

public static string method (Pair pairex)

{

System.out.println("Pair string pairex");

    return "  "

    }

public static int method (Pair pairex)

{

System.out.println("Pair int pairex");

    return 1;

    }

}

这两个方法就可以照常运行,因为通过不同的参数列表,通过了编译器,而后又通过不同的返回值,使得虚拟机也可以区分。

7.桥方法不仅用于泛型类型。 在一个方法覆盖另一个方法时可以指定一个更严格的返回类型。例如:

public class Employee implements Cloneable

    {

        public Employee clone() throws CloneNotSupportedException { ...}

    }

Object.clone 和 Employee.clone 方法被说成具有协变的返回类型(covariant returntypes)。 实际上,Employee 类有两个克隆方法:

Employee clone() // defined above

Object clone() // 合成的桥方法,覆盖了原本的Object.clone方法

合成的桥方法中调用了新定义的方法。

相关文章

  • 2018-06-02 8.5 虚拟机对泛型代码的处理

    以下部分只是将我在学习笔记中的关于Java核心技术卷一 8.5 的笔记单拆出来,形成一篇文章。 8.5 泛型代码和...

  • Swift泛型笔记

    泛型可以让代码处理类型更加灵活,在某些场景下可以很大程度的重用代码,泛型是什么,使用泛型的好处,这里不多说,网上有...

  • Java泛型—Java语法糖,只在编译有作用,编译后擦出泛型

    Java泛型—Java语法糖,只在编译有作用,编译后擦出泛型 在代码进入和离开的边界处,会处理泛型 Java泛型作...

  • Java泛型 --(1)泛型基础篇

    主要内容 (1)能够定义简单的泛型类、泛型方法(2)知晓类型变量的限定规则和应用(3)理解虚拟机是如何解析泛型代码...

  • Flutter中泛型

    泛型 • 正确指定泛型类型有助于产生更好代码 • 泛型减少重复代码 而泛型就可以解决上面的问题,它通过对类型参数化...

  • 008-自定义泛型,Collections

    自定义泛型 泛型类 代码实现 测试 泛型接口 代码实现 泛型方法 代码演示 测试 泛型上下边界 Collectio...

  • JAVA泛型•虚拟机执行泛型代码

    为了更好地阅读体验,欢迎访问博客原文 虚拟机中类型擦除 Java虚拟机是不存在泛型类型对象的,所有的对象都属于普通...

  • 泛型依赖注入

    泛型依赖注入 1.定义泛型 Java代码: 2.实现基类,声明泛型 Java代码: xml代码:

  • Swift探索( 九): 泛型

    一: 泛型 1.1 什么是泛型 泛型可以将类型参数化,提高代码复用效率,减少代码量。 1.2 泛型解决的问题 下面...

  • swift泛型

    一、以泛型为参数的函数泛型是Swift语言强大的核心,泛型是对类型的抽象,使用泛型开发者可以更加灵活方便的表达代码...

网友评论

      本文标题:2018-06-02 8.5 虚拟机对泛型代码的处理

      本文链接:https://www.haomeiwen.com/subject/grulsftx.html