- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章详解Java 中泛型的实现原理由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
泛型是 Java 开发中常用的技术,了解泛型的几种形式和实现泛型的基本原理,有助于写出更优质的代码。本文总结了 Java 泛型的三种形式以及泛型实现原理.
泛型的本质是对类型进行参数化,在代码逻辑不关注具体的数据类型时使用。例如:实现一个通用的排序算法,此时关注的是算法本身,而非排序的对象的类型.
如下定义了一个泛型方法, 声明了一个类型变量,它可以应用于参数,返回值,和方法内的代码逻辑.
1
2
3
4
5
|
class
GenericMethod{
public
<T> T[] sort(T[] elements){
return
elements;
}
}
|
与泛型方法类似,泛型类也需要声明类型变量,只不过位置放在了类名后面,作用的范围包括了当前中的成员变量类型,方法参数类型,方法返回类型,以及方法内的代码中.
子类继承泛型类时或者实例化泛型类的对象时,需要指定具体的参数类型或者声明一个参数变量。如下,SubGenericClass 继承了泛型类 GenericClass,其中类型变量 ID 的值为 Integer,同时子类声明了另一个类型变量 E,并将E 填入了父类声明的 T 中.
1
2
3
4
5
6
7
|
class
GenericClass<ID, T>{
}
class
SubGenericClass<T>
extends
GenericClass<Integer, T>{
}
|
泛型接口与泛型类类似,也需要在接口名后面声明类型变量,作用于接口中的抽象方法返回类型和参数类型。子类在实现泛型接口时需要填入具体的数据类型或者填入子类声明的类型变量.
1
2
3
|
interface
GenericInterface<T> {
T append(T seg);
}
|
泛型本质是将数据类型参数化,它通过擦除的方式来实现。声明了泛型的 .java 源代码,在编译生成 .class 文件之后,泛型相关的信息就消失了。可以认为,源代码中泛型相关的信息,就是提供给编译器用的。泛型信息对 Java 编译器可以见,对 Java 虚拟机不可见.
Java 编译器通过如下方式实现擦除:
Java 官方文档原文 。
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods. Insert type casts if necessary to preserve type safety. Generate bridge methods to preserve polymorphism in extended generic types. 。
下面通过具体代码来说明 Java 中的类型擦除.
实验原理:先用 javac 将 .java 文件编译成 .class 文件,再使用反编译工具 jad 将 .class 文件反编成回 Java 代码,反编译出来的 Java 代码内容反映的即为 .class 文件中的信息.
如下源代码,定义 User 类,实现了 Comparable 接口,类型参数填入 User,实现 compareTo 方法.
1
2
3
4
5
6
7
|
class
User
implements
Comparable<User> {
String name;
public
int
compareTo(User other){
return
this
.name.compareTo(other.name);
}
}
|
JDK 中 Comparable 接口源码内容如下:
1
2
3
4
|
package
java.lang;
public
interface
Comparable<T>{
int
compareTo(T o);
}
|
我们首先反编译它的接口,Comparable 接口的字节码文件,可以在 $JRE_HOME/lib/rt.jar 中找到,将它复制到某个目录。使用 jad.exe(需要另外安装)反编译这个 Comparable.class 文件.
1
|
$ jad Comparable.
class
|
反编译出来的内容放在 Comparable.jad 文件中,文件内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: Comparable.java
package
java.lang;
// Referenced classes of package java.lang:
// Object
public
interface
Comparable
{
public
abstract
int
compareTo(Object obj);
}
|
对比源代码 Comparable.java 和反编译代码 Comparable.jad 的内容不难发现,反编译之后的内容中已经没有了类型变量 T 。compareTo 方法中的参数类型 T 也被替换成了 Object。这就符合上面提到的第 1 条擦除原则。这里演示的是用 Object 替换类型参数,使用界定类型替换类型参数的例子可以反编译一下 Collections.class 试试,里面使用了大量的泛型.
使用 javac.exe 将 User.java 编译成 .class 文件,然后使用 jad 将 .class 文件反编译成 Java 代码.
1
2
|
$ javac User.java
$ jad User.
class
|
User.jad 文件内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: User.java
class
User
implements
Comparable
{
User()
{
}
public
int
compareTo(User user)
{
return
name.compareTo(user.name);
}
// 桥接方法
public
volatile
int
compareTo(Object obj)
{
return
compareTo((User)obj);
}
String name;
}
|
对比编辑的源代码 User.java 和反编译出来的代码 User.jad,容易发现:类型参数没有了,多了一个无参构造方法,多了一个 compareTo(Object obj) 方法,这个就是桥接方法,还可以发现参数 obj 被强转成 User 再传入 compareTo(User user) 方法。通过这些内容可以看到擦除规则 2 和规则 3 的实现方式.
强转规则比较好理解,因为泛型被替换成了 Object,要调用具体类型的方法或者成员变量,当然需要先强转成具体类型才能使用。那么插入的桥接方法该如何理解呢?
如果我们只按照下面方式去使用 User 类,这样确实不需要参数类型为 Object 的桥接方法.
1
2
3
|
User user =
new
User();
User other =
new
User();
user.comparetTo(other);
|
但是,Java 中的多态特性允许我们使用一个父类或者接口的引用指向一个子类对象.
1
|
Comparable<User> user =
new
User();
|
而按照 Object 替换泛型参数原则,Comparable 接口中只有 compareTo(Object) 方法,假设没有桥接方法,显然如下代码是不能运行的。所以 Java 编译器需要为子类(泛型类的子类或泛型接口的实现类)中使用了泛型的方法额外生成一个桥接方法,通过这个方法来保证 Java 中的多态特性.
1
2
3
|
Comparable<User> user =
new
User();
Object other =
new
User();
user.compareTo(other);
|
而普通类中的泛型方法在进行类型擦除时不会产生桥接方法。例如:
1
2
3
4
|
class
Dog{
<T>
void
eat(T[] food){
}
}
|
类型擦除之后变成了:
1
2
3
4
5
6
7
8
9
10
11
|
class
Dog
{
Dog()
{
}
void
eat(Object aobj[])
{
}
}
|
Java 中的泛型有 3 种形式,泛型方法,泛型类,泛型接口。Java 通过在编译时类型擦除的方式来实现泛型。擦除时使用 Object 或者界定类型替代泛型,同时在要调用具体类型方法或者成员变量的时候插入强转代码,为了保证多态特性,Java 编译器还会为泛型类的子类生成桥接方法。类型信息在编译阶段被擦除之后,程序在运行期间无法获取类型参数所对应的具体类型.
https://docs.oracle.com/javase/tutorial/java/generics/index.html 。
https://stackoverflow.com/questions/25040837/generics-bridge-method-on-polymorphism 。
以上就是详解Java 中泛型的实现原理的详细内容,更多关于Java 泛型实现原理的资料请关注我其它相关文章! 。
原文链接:https://www.cnblogs.com/robothy/p/13949788.html 。
最后此篇关于详解Java 中泛型的实现原理的文章就讲到这里了,如果你想了解更多关于详解Java 中泛型的实现原理的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
可以用这种方式转换字符串吗?我们有相同的参数,Java 做出了正确的选择。如果值是整数 - 我们调用 parseInt(value),否则如果值是 double 型 - 我们调用 parseDoubl
如果这段代码中有一个愚蠢的错误,我提前道歉,但我似乎无法解决它。我的问题是这样的,我用GCC-8(通过home-brew安装在Mac上)编译,然后在终端中执行。当使用 int do 定义变量 s &
我用 a-videosphere 制作了一个 a-scene。我尝试使用按钮启用/禁用声音,但有些想法不起作用?这是我的代码: var gargamel = 0; function
我正在使用 ISAAC 实现来生成随机整数。我需要用这些整数创建一个高斯值。首先,我需要将它们从 0 更改为 1 的 double 值。我怎样才能在Java中做到这一点?这是到目前为止我将整数转换为
我将 0x0000 到 0x01c2 范围内的十六进制值从 BLE 获取到我的手机 a 作为字符串。为了将其绘制在图表中,我必须将其转换为 double,我已经尝试过 this method但遗憾的是
我有一个父类(super class) Animal和一个子类 Dog 。在第三节课中,我有一个 List它同时接受子类型和父类(super class)型对象。 public class foo{
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 6 年前。 Improve this ques
我正在尝试查询我的用户的距离。我可以用这段代码做到这一点 PFGeoPoint.geoPointForCurrentLocationInBackground { (geoPoint: PFGe
考虑示例:http://jsfiddle.net/KWLu7/16/ 我正在尝试将总体重要性值计算为每个选定选择重要性的总和乘以其父标准重要性: var watch = $scope.$watch("
这个问题在这里已经有了答案: Bounding generics with 'super' keyword (6 个答案) 关闭 2 年前。 我有一个列表装饰器,它应该允许从一个列表转换到另一个列表
为什么下面的代码没有选择最近父类(super class)型的隐式 val? class A class B extends A trait TC[-T] { def show(t: T): Stri
这是我想要做的 def merge[A, B, C](eithers: Either[A,B]*)(implicit ev1: A x, x => x)) 关于scala - 推断常见的父类(s
我正在尝试从具有 double 类型列的Cassandra表中获取 double 值。我已经使用CQL3语法创建了表: CREATE TABLE data_double ( datetime
是否应该在不需要显式类型定义的情况下编译以下 this ? def prepList[B >: A](prefix: PlayList[B]) : PlayList[B] = prefix.fol
我正在查看某人的代码,并且在创建结构时使用了 abstract type AbstractFoo end julia> struct Foo1 struct Foo2 foo_op(x::Abst
一些示例代码: public class Main { class SomeType { } class A { protected T createSome
是否可以只接受类的泛型类型的父类(super class)型? 我正在寻找的是这样的: class MyClass { public void myMethod(TS someObject
在我的代码中,我有许多 ArrayList 被传递到排序方法中。每个 ArrayList 都有不同的泛型类型,但所有这些类型都是 Sorter 的实现。排序方法旨在接受 Sorter 类型的 Arra
如果已经有人问过这个问题,请链接并关闭这个问题。 我目前正在为另一个使用起来复杂得多(并且有潜在危险)的 API 的简化 API 设计原型(prototype)。 考虑到相关的有点复杂的对象创建,我决
我正在尝试构建一个具有某些依赖项的 android 应用程序,但是其中一个导致了此错误: Illegal class file: Class module-info is missing a supe
我是一名优秀的程序员,十分优秀!