无法重现类型擦除示例的结果

2024-04-08

我正在阅读“Java 泛型和集合”第 8.4 节。作者在尝试解释二进制兼容性时定义了以下代码:

interface Name extends Comparable {
    public int compareTo(Object o);
}
class SimpleName implements Name {
    private String base;
    public SimpleName(String base) {
        this.base = base;
    }
    public int compareTo(Object o) {
        return base.compareTo(((SimpleName)o).base);
    }
}
class ExtendedName extends SimpleName {
    private String ext;
    public ExtendedName(String base, String ext) {
        super(base); this.ext = ext;
    }
    public int compareTo(Object o) {
        int c = super.compareTo(o);
        if (c == 0 && o instanceof ExtendedName)
        return ext.compareTo(((ExtendedName)o).ext);
        else
        return c;
    }
}
class Client {
    public static void main(String[] args) {
        Name m = new ExtendedName("a","b");
        Name n = new ExtendedName("a","c");
        assert m.compareTo(n) < 0;
    }
}

然后讨论使 Name 接口和 SimpleName 类通用并保留 ExtendedName 不变。结果新代码是:

interface Name extends Comparable<Name> {
    public int compareTo(Name o);
}
class SimpleName implements Name {
    private String base;
    public SimpleName(String base) {
        this.base = base;
    }
    public int compareTo(Name o) {
        return base.compareTo(((SimpleName)o).base);
    }
}
// use legacy class file for ExtendedName
class Test {
    public static void main(String[] args) {
        Name m = new ExtendedName("a","b");
        Name n = new ExtendedName("a","c");
        assert m.compareTo(n) == 0; // answer is now different!
    }
}

作者将这种行为的结果描述如下:

假设我们泛化 Name 和 SimpleName,以便它们定义 compareTo(Name),但我们没有 ExtendedName 的来源。既然它定义了 仅compareTo(Object),调用compareTo(Name)而不是compareTo(Object)的客户端代码将调用SimpleName(定义它的位置)上的方法,而不是 ExtendedName(未定义的地方),因此将比较基本名称,但 忽略扩展名。

但是,当我仅将 Name 和 SimpleName 设为通用时,我会收到编译时错误,而不是作者上面描述的错误。错误是:

名称冲突:NameHalfMovedToGenerics.ExtendedName 中的compareTo(Object) 和Comparable 中的compareTo(T) 具有相同的擦除,但两者都不覆盖另一个

这并不是我第一次遇到这样的问题 - 早些时候,在尝试阅读有关擦除的 Sun 文档时,我遇到了类似的问题,即我的代码没有显示与作者描述的结果相同的结果。

我是否对作者的意思理解错误?

任何帮助都感激不尽。

提前致谢。


这是在以下情况下可能发生的问题的示例单独编译.

单独编译的主要微妙之处在于,当编译调用者类时,某些信息将从被调用者复制到调用者的类文件中。如果调用者稍后针对不同版本的被调用者运行,则从旧版本的被调用者复制的信息可能与新版本的被调用者不完全匹配,并且结果可能会有所不同。仅通过查看源代码很难看出这一点。这个例子展示了当进行这样的修改时,程序的行为如何以令人惊讶的方式发生变化。

在示例中,Name and SimpleName被修改并重新编译,但旧的编译二进制文件ExtendedName至今仍在使用。这就是“源代码”的真正含义ExtendedName不可用。”当针对修改后的类层次结构编译程序时,它记录的信息与针对旧层次结构编译时记录的信息不同。

让我详细介绍一下我为重现此示例而执行的步骤。

在一个空目录中,我创建了两个子目录v1 and v2. In v1我将第一个示例代码块中的类放入单独的文件中Name.java, SimpleName.java, and ExtendedName.java.

请注意,我没有使用v1 and v2目录作为包。所有这些文件都位于未命名的包中。另外,我使用单独的文件,因为如果它们都是嵌套类,则很难单独重新编译其中的一些文件,这对于示例的工作是必要的。

另外我将主程序重命名为Test1.java并修改如下:

class Test1 {
    public static void main(String[] args) {
        Name m = new ExtendedName("a","b");
        Name n = new ExtendedName("a","c");
        System.out.println(m.compareTo(n));
    }
}

In v1我编译了所有内容并运行 Test1:

$ ls
ExtendedName.java  Name.java  SimpleName.java  Test1.java
$ java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
$ javac *.java
$ java Test1
-1

Now, in v2我把Name.java and SimpleName.java文件,使用泛型进行修改,如第二个示例代码块所示。我也复制进去v1/Test1.java to v2/Test2.java并相应地重命名该类,但除此之外代码是相同的。

$ ls
Name.java  SimpleName.java  Test2.java
$ javac -cp ../v1 *.java
$ java -cp .:../v1 Test2
0

这表明结果m.compareTo(n)之后就不同了Name and SimpleName进行了修改,同时使用旧的ExtendedName二进制。发生了什么?

我们可以通过查看反汇编的输出来看到差异Test1类(针对旧类编译)和Test2类(针对新类编译)以查看为该类生成了哪些字节码m.compareTo(n)称呼。还在v2:

$ javap -c -cp ../v1 Test1
...
29: invokeinterface #8,  2     // InterfaceMethod Name.compareTo:(Ljava/lang/Object;)I
...

$ javap -c Test2
...
29: invokeinterface #8,  2     // InterfaceMethod Name.compareTo:(LName;)I
...

编译时Test1,将信息复制到Test1.class文件是对compareTo(Object)因为这就是方法Name界面已至此。使用修改后的类,编译Test2结果是调用的字节码compareTo(Name)因为这就是修改后的内容Name界面现在有了。什么时候Test2运行时,它会寻找compareTo(Name)方法并因此bypasses the compareTo(Object)方法中的ExtendedName上课、打电话SimpleName.compareTo(Name)反而。这就是行为不同的原因。

注意老人的行为Test1 binary不改变:

$ java -cp .:../v1 Test1
-1

But if Test1.java针对新的类层次结构重新编译,它的行为将会改变。本质上就是这样Test2.java是,但具有不同的名称,以便我们可以轻松地看到运行旧二进制文件和重新编译版本之间的区别。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

无法重现类型擦除示例的结果 的相关文章

随机推荐

  • 在 Linux 上调用 Python IDLE

    我正在尝试搭建Python开发环境 我在 Linux Mint 14 上运行 Python 2 7 3 我下载了 IDLE 并安装了它 它干净安装并且 Python 运行得很好 那么如何调用IDLE呢 我的路径中没有任何 IDLE whic
  • 如何运行使用 PyRun 读入 std::string 的 python 文件

    我正在将 Python 嵌入到我的 C 程序中 并且非常有效地使用了 PyRun SimpleString 但现在遇到了麻烦 我所做的是将 python py 文件加载到 std string 但现在运行它时遇到问题 PyRun Simpl
  • Git post-rebase 钩子

    有没有像 post rebase hook 这样的东西 我的目标是我想要一个脚本在之后执行git pull rebase使用命令 看来重写后挂钩可以完成这项工作
  • 领域驱动设计和聚合参考

    我正在设计领域模型 但有些东西似乎不太好 我从一个主要的聚合开始 它引用了其他聚合 而其他聚合也引用了更多聚合 我可以从主聚合开始遍历孔域模型 我看到的问题是我将在内存中保存聚合的所有实例 这是一个好的设计吗 我可以通过延迟加载解决内存问题
  • Postgres jsonb_set 多个嵌套字段

    我有一个带有 jsonb 列的数据库表 该列有一个实体 带有嵌套的子实体 假设我们有 SELECT jsonb set top nested leaf 1 top nested leaf 2 更新哪个作品就可以了top nested lea
  • SFSpeechRecognizer 的多个实例?

    我正在尝试运行多个实例SFSpeechRecognizer 不幸的是我总是收到这个错误 Utility AFAggregator logDictationFailedWithError Error Domain kAFAssistantEr
  • 循环遍历空文本框,直到文本框有数据

    我确信对此有一个简单的解决方案 但我没有想到 我有一个带有三个文本框的表单 在运行主代码之前 我想确保每个文本框中都有数据 我已将 hasData 初始化为变量 该变量将决定代码是否可以继续前进 我在 Do While 循环中评估 hasD
  • 与 fetch_all 一起使用时,存储过程导致 mysqli 出现问题

    我已经将这个问题分解到其本质 但仍然遇到问题 当我尝试使用 fetch all 获取存储过程的结果时 我得到的结果按预期返回到数组 但后续 mysqli 调用抛出 命令不同步 错误 我什至尝试简化我的存储过程 CREATE PROCEDUR
  • ImageMagick 没有此图像格式的解码委托

    我在 Windows 和 wamp 服务器下工作 这是我使用 Imagick 的 PHP 代码 imagick new Imagick SERVER DOCUMENT ROOT this gt name where SERVER DOCUM
  • 使用 UITableView 登录

    如何像在 Skype 应用程序中一样创建用户名 密码登录 我知道这是一个分组表视图 但我该怎么做呢 我搜索了网站 发现了以下代码 UITableViewCell tableView UITableView tableView cellFor
  • 将 RadioButton IsChecked 绑定到 ListBoxItem IsSelected 和 ListBox IsFocused

    我见过其他与此非常相似的问题 但不知何故我仍然无法让它发挥作用 这是场景 我拥有的我有一个ListBox显示我的视图模型的列表 每个视图模型都有一个子级列表 这些子级显示在另一个嵌套列表框中 我正在使用一个DataTemplate为了达成这
  • 在 React Native 部分列表中过滤数据

    我正在使用 React Native 的SectionList SectionList 的数据看起来像这样 data title Asia data Taj Mahal Great Wall of China Petra title Sou
  • 如何修复 pg_dump 版本不匹配错误?

    当尝试将本地数据获取到 Heroku 时 我遇到两个不同版本的 pg dump 之间的版本不匹配 具体来说 我收到以下消息 pg dump server version 9 2 2 pg dump version 9 1 4 pg dump
  • Firefox:image.onload 中的 canvas.toDataURL 但返回透明图像

    我知道图像必须完整 在画布上使用 toDataURL 函数之前其加载已完成 将代码放在 image onload 函数中可确保这一点 还尝试了 canvas getContext 中的 preserveDrawingBuffer true
  • Angular2 错误处理最佳实践

    我有一个关于 Angular2 错误处理最佳实践的问题 这是我用来捕获错误的代码 Getdata data object let body JSON stringify data let headers new Headers Conten
  • 如何从 Java 程序动态创建新的 .java 文件?

    我想从 Java 程序创建一个 java 当我运行该程序时 将在我的项目中自动创建一个 Java 文件 并在该文件中创建一些运行时 动态 变量 我怎样才能做到这一点 我知道为此我必须使用 Reflection API 例如Class and
  • Qt5.8.0缺少vcruntime140d_app.dll

    我有一个非常简单的 Qt 应用程序 由 main cpp mainwindow cpp mainwindow h 和 mainwindow ui 组成 各内容如下所示 main cpp include mainwindow h includ
  • WordPress jQuery 未捕获类型错误:对象 [object Object] 的属性“$”不是函数

    我正在将 html 文件转换为 WordPress 主题 并使用插件 ZClip 将文本复制到剪贴板 ZClip 插件在我的 html 演示中工作正常 但是当转换为 WordPress 时 我在第 288 行中收到了这个奇怪的语法错误 Un
  • 从 Crystal Report 中的多个表推送数据

    我有一个名为 CR1 的水晶报表 现在我想将多个表中的数据填充到我的水晶报表 CR1 中 我使用的是VS2008 编码语言是ASP net中的C 任何帮助将不胜感激 创建一个存储过程然后将其用作报告的数据源 我会帮你的
  • 无法重现类型擦除示例的结果

    我正在阅读 Java 泛型和集合 第 8 4 节 作者在尝试解释二进制兼容性时定义了以下代码 interface Name extends Comparable public int compareTo Object o class Sim