Java、静态方法绑定和泛型都与一些方法重载结合在一起

2024-01-16

正如标题所暗示的,我的问题有点奇怪和复杂。我知道我要做的事情违反了“良好”编程实践的所有规则,但是嘿,如果我们不活一点,生活会怎样呢?

所以我所做的是创建以下程序。 (请注意,这是一个更大的实验的一部分,旨在真正尝试和理解泛型,因此某些函数名称可能有点乱)

import java.util.*;

public class GenericTestsClean 
{
    public static void test2()
    {
        BigCage<Animal> animalCage=new BigCage<Animal>();
        BigCage<Dog> dogCage=new BigCage<Dog>();
        dogCage.add(new Dog());
        animalCage.add(new Cat());
        animalCage.add(new Dog());
        animalCage.printList(dogCage);
        animalCage.printList(animalCage);
    }


    public static void main(String [] args)
    {
        //What will this print
        System.out.println("\nTest 2");
        test2();
    }

}

class BigCage<T> extends Cage<T>
{

    public static <U extends Dog> void printList(List<U> list)
    {
        System.out.println("*************"+list.getClass().toString());
        for(Object obj : list)
            System.out.println("BigCage: "+obj.getClass().toString());
    }

}
class Cage<T> extends ArrayList<T>
{
    public static void printList(List<?> list)
    {
        System.out.println("*************"+list.getClass().toString());
        for(Object obj : list)
            System.out.println("Cage: "+obj.getClass().toString());
    }
}

class Animal
{
}
class Dog extends Animal
{
}
class Cat extends Animal
{
}

现在令我困惑的是,这可以很好地编译javac 1.6.0_26但是当我运行它时,我得到以下类转换异常:

Test 2
*************class BigCage
BigCage: class Dog
*************class BigCage
Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to Dog
        at BigCage.printList(GenericTestsClean.java:31)
        at GenericTestsClean.test2(GenericTestsClean.java:13)
        at GenericTestsClean.main(GenericTestsClean.java:21)

这里需要注意一些事项:

  1. 两个 printList 是NOT按预期覆盖但互相重载(它们具有不同的类型,因为它们的参数的泛型类型不同)。这可以通过使用 @Override 注解来验证
  2. 改变void printList(List<?>)中的方法笼类非静态会产生适当的编译时错误
  3. 改变方法void <U extends Dog> printList(List<U>) in 大笼级 to void <U> printList(List<U>)生成适当的错误。
  4. In main()呼叫打印列表()通过大笼级(即 BigCage.printList(...))生成相同的运行时错误
  5. In main()呼叫打印列表()通过笼类(即 Cage.printList(...)) 按预期工作,仅调用以下版本打印列表 in Cage
  6. 如果我复制定义printList(List<?>) to 大笼级 from 笼类,这将隐藏定义笼类,我得到适当的编译器错误

现在,如果我不得不在黑暗中猜测这里发生了什么,我会说编译器搞砸了,因为它分多个阶段工作:类型检查 and 重载方法解析。在类型检查阶段,我们通过了有问题的行,因为大笼级遗传void printList(List<?>) from class Cage它将匹配我们扔给它的任何旧列表,所以确保我们有一个可以工作的方法。然而,一旦需要用实际调用的方法来解决时,我们就会遇到由于类型擦除而导致的问题BigCage.printList and Cage.printList具有完全相同的签名。这意味着当编译器正在寻找匹配项时animalCage.printList(animalCage);它会选择它匹配的第一个方法(如果我们假设它从 BigCage 的底部开始并一直工作到 Object),它会发现void <U extends Dog> printList(List<U>)首先而不是正确的匹配void printList(List<?>)

现在我真正的问题:我这里离真相有多近?这是一个已知的错误?这是一个错误吗?我知道如何解决这个问题,这更多的是一个学术问题。

**EDIT**

正如下面很少有人发布的,这段代码可以在 Eclipse 中运行。 我的具体问题涉及 javac 版本 1.6.0_26。还有,我不是 确定在这种情况下我是否完全同意 Eclipse,即使它 有效,因为添加printList(List<?>) to BigCage将要 导致 Eclipse 中出现编译时错误,我不明白原因 当相同的方法被继承而不是手动时它应该起作用 添加(参见Note 6 above).


考虑这个小问题:

class A
{
    static void foo(){ }
}
class B extends A
{
    static void foo(){ }
}
void test()
{
    A.foo();
    B.foo();
}

假设我们删除foo方法来自B,我们只需重新编译B本身,当我们跑步时会发生什么test()?它是否应该抛出链接错误,因为B.foo()没有找到吗?

根据 JLS3 #13.4.12,删除B.foo不会破坏二进制兼容性,因为A.foo仍然被定义。这意味着,当B.foo()被执行,A.foo()被调用。请记住,没有重新编译test(),所以这个转发必须由JVM来处理。

相反,我们删除foo方法来自B,并重新编译全部。即使编译器静态地知道B.foo()实际上意味着A.foo(),它仍然生成B.foo()在字节码中。目前,JVM 将转发B.foo() to A.foo()。但如果将来B获得新的foo方法,新方法将在运行时被调用,即使test()没有重新编译。

从这个意义上说,静态方法之间存在着压倒一切的关系。当编译看到B.foo(),它必须将其编译为B.foo()在字节码中,无论是否B has a foo() today.

在你的例子中,当编译器看到BigCage.printList(animalCage),它正确地推断出它实际上正在调用Cage.printList(List<?>)。所以它需要将调用编译为字节码:BigCage.printList(List<?>)- 目标类别必须是BigCage在这里而不是Cage.

哎呀!字节码格式尚未升级以处理此类方法签名。泛型信息作为辅助信息保留在字节码中,但对于方法调用,这是旧的方式。

擦除发生。该调用实际上被编译成BigCage.printList(List)。太糟糕了BigCage还有一个printList(List)擦除后。在运行时,该方法被调用!

这个问题是由于Java规范和JVM规范不匹配造成的。

Java 7 稍微收紧了;意识到字节码和 JVM 无法处理这种情况,它不再编译你的代码:

错误:名称冲突: BigCage 中的 printList(List) 和 Cage 中的 printList(List) 有 同样的擦除,但都没有隐藏 其他

另一个有趣的事实:如果这两个方法具有不同的返回类型,您的程序将正常工作。这是因为在字节码中,方法签名包括返回类型。所以两者之间不存在混淆Dog printList(List) and Object printList(List)。也可以看看Java 中的类型擦除和重载:为什么会起作用? https://stackoverflow.com/questions/5527235/type-erasure-and-overloading-in-java-why-does-this-work/5528802#5528802这个技巧只在 Java 6 中允许。Java 7 禁止它,可能是出于技术原因以外的原因。

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

Java、静态方法绑定和泛型都与一些方法重载结合在一起 的相关文章

  • 如何从 WifiP2pDeviceList 获取 wifi direct 设备名称

    我想在执行请求对等点时获取 wi fi direct 名称 这是我的代码 if WifiP2pManager WIFI P2P PEERS CHANGED ACTION equals action Log d tag success dis
  • 如何让Spring RabbitMQ创建一个新的队列?

    根据我对rabbit mq的 有限 经验 如果您为尚不存在的队列创建新的侦听器 则会自动创建该队列 我正在尝试将 Spring AMQP 项目与rabbit mq 一起使用来设置侦听器 但出现错误 这是我的 xml 配置
  • Java:高性能消息传递(单生产者/单消费者)

    我最初问这个问题here https stackoverflow com questions 3367192 java is while true loop in a thread bad whats the alternative 但我意
  • 在Java中清空数组/处理

    除了循环遍历数组中的每个元素并将每个元素设置为 null 之外 Java 处理中是否有一个本机函数可以简单地清空数组 或销毁它 以便能够将其重新声明为新数组 There s Arrays fill myArray null 并不是说它执行的
  • 将 spring-security 与 spring-webflux 结合使用时禁用 WebSession 创建

    我正在使用 Rest api 运行无状态 spring boot 应用程序 并希望按照所述禁用 WebSessions 的创建https www baeldung com spring security session https www
  • 静态方法的 Java 内存模型

    我来自操作系统和 C 语言背景 在代码编译时 世界很简单 需要处理和理解堆栈 堆文本部分等 当我开始学习 Java 时 我确实了解 JVM 和垃圾收集器 我对静态方法感到很有趣 根据我的理解 类的所有实例都会在堆中创建 然后被清理 但是 对
  • @OneToMany 与 @JoinTable 错误

    我试图理解 OneToMany with JoinTable 对于这样的场景 我正在使用 JPA 2 1 Hibernate 5 0 4 和 Oracle 11 XE 当我打电话时userDao save user 下面的代码 我有 jav
  • RSA SignatureException:签名长度不正确

    我在签署 rsa 签名时遇到问题 我有一个用私钥加密的签名 然而 当我尝试使用公钥验证它时遇到问题 我得到以下异常 java security SignatureException Signature length not correct
  • 哈希码是否用于加速集合中的对象查找?

    IIUC 相同类型的两个不同对象可以存储在 HashSet 中 即使两个对象在以下情况下返回相同的值 hashCode 叫做 例如根据本文 https eclipsesource com blogs 2012 09 04 the 3 thi
  • 是否可以从另一个方法传递 args[] 来调用 main 方法?

    我试图从另一个传递参数的方法调用类的主要方法 就像从命令行运行该类时一样 有没有办法做到这一点 您可以致电main方法就像您调用任何其他 静态 方法一样 MyClass main new String arg1 arg2 arg3 Exam
  • Java 唤醒休眠线程

    我阅读了其他帖子 但没有找到我正在寻找的确切答案 所以我希望有人能给出一些澄清 我有一个将运行一段时间的程序 我有一些在后台运行的线程来执行各种任务 为了简单起见 让我们考虑 3 个线程 ThreadA每 10 秒执行一次任务 其中Thre
  • BlackBerry SQLite:将一个 SQLite 数据库连接到另一个

    我正在尝试使用 SQLite 将一个 SQLite 数据库附加到 BlackBerry 上的另一个数据库附加数据库 http www sqlite org lang attach html命令 Database d1 d2 Statemen
  • 不要模拟值对象:过于通用的规则,没有解释

    以下是 Mockito 单元测试框架的引用 不要模拟值对象 为什么有人会想要这样做呢 因为实例化对象太痛苦了 gt 无效 原因 如果创造新的装置太困难 那就是一个迹象 代码可能需要一些认真的重构 另一种方法是创建 价值对象的构建者 有一些工
  • 如何从 Google Custom Search API 获取超过 100 个结果

    我正在尝试使用 Google Custom Search API 在 Java 中进行研究 因此 我需要为每个查询提供一个大的结果集 然而 我似乎仅限于前 100 个结果 这比我需要的要少得多 我使用这样的列表方法 list setStar
  • 为什么现在()? (客观化)

    为什么我想要异步加载 Objectify 实体 异步加载到底意味着什么 根据客观化有关加载的文档 https code google com p objectify appengine wiki BasicOperations Loadin
  • scala中的协变类型参数需要在java接口中保持不变

    我有一个看起来像这样的特征 一些进一步的信息可以在我自己提出了这个相关问题 https stackoverflow com questions 3695990 inheritance and automatic type conversio
  • H2 - (相当)长的 INSERT 失败,错误 42000

    H2 内存中 插入 错误 42000 尝试过版本 1 4 196 1 4 197 1 4 199 我还尝试在 H2 服务器 本地 上执行 INSERT 也失败 给出错误的行 抱歉 但出于安全原因 我无法生成更多 INSERT INTO tb
  • 将带有 webapp 的 WAR 部署到 Maven 中央存储库是否有意义?

    这样做有意义吗 如果是 我在哪里可以找到使用简单的 Web Hello World 执行此操作的示例 当人们从 Maven 执行 Web 应用程序时 他们会使用 Jetty 来运行它吗 我想 tomcat 太重了 任何帮助将不胜感激 谢谢
  • JMockit - 初始化问题

    当我使用以下测试时 我收到警告 警告 JMockit 是按需初始化的 这可能会导致某些测试失败 请检查文档以获取更好的初始化方法 这是我的测试实现 package test import static mockit Mockit impor
  • H2 用户定义的聚合函数 ListAgg 不能在第一个参数上使用 DISTINCT 或 TRIM()

    所以我有一个 DB2 生产数据库 我需要在其中使用可用的函数 ListAgg 我希望使用 H2 的单元测试能够正确测试此功能 不幸的是H2不直接支持ListAgg 但是 我可以创建一个用户定义的聚合函数 import java sql Co

随机推荐

  • 重新打包 .jar 文件

    我需要将 JRE7 库中的一些 jar 添加到我的 Android 项目中 但例如rt jar与以下内容相冲突android jar来自 Adroid 2 2 SDK 所以我收到此错误 不明智或错误地使用核心类 java 或 javax 当
  • 适用于 Linux 和 PHP 5.4 的 MSSQL Server 本机 ODBC 驱动程序

    我在 Linux Debian 6 x64 上有 Apache 2 2 16 和 PHP 5 4 3 要安装适用于 Linux 的 MSSQL Server 的本机 ODBC 驱动程序 我使用以下说明 http www codesynthe
  • 如何强制重新编译 Linq to SQL 查询的执行计划?

    我有一个动态创建的 LINQ to SQL 查询 有趣的是 当我在 SQL Management Studio 中运行它时 速度快如闪电 当我从 L2S 运行它时 一段时间后它变得非常慢 这可能是由于查询计划 执行计划造成的 当我重新启动
  • 连接两个表时索引如何提高性能

    在我们的一篇练习论文中 我们被问到了标题中的问题 我读过的大多数文章都说索引可以提高连接的性能 但没有告诉我如何提高 也许这是显而易见的事情 不需要说明 索引本质上是对列进行排序 对吗 所以我想按顺序排列一列会更容易操作 还有更多吗 还是我
  • 以简单的方式将 SQL 转换为 SQLite? [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我的问题 有人有一种超级简单的方法在
  • 主机名未解析为本地 IP 地址

    我正在 vmware Fusion 中运行 Windows 8 虚拟机 它在运行 OSX 10 10 Yosemite 的 Mac 内运行 VM 的计算机名称为 Proud 当我从内部 ping 虚拟机时 即ping a 192 168 0
  • 基于逗号分隔字符向量列值的Python熔化数据框

    我目前正在进行一项测试 其中有不同的区域和一些相关的统计数据 以及位于这些区域的以逗号分隔的基因列表 该列表的数量是可变的 并且可能不包含任何内容 NA 我怎样才能 融化 这个数据框 region id statistic genelist
  • 使用 Excel VBA 编写和格式化 Word 文档

    我正在尝试使用 Excel VBA 编写 Word 文档 我可以创建一个Word文档 向其中写入文本 更改样式都不是问题 我想做的是将一些文本居中 但我一生都无法弄清楚 这是我用来编写文档的代码 Set wrdApp CreateObjec
  • 显示并关闭模态视图控制器 UIImagePickerController 之后,我的 Cocos2d iPhone 应用程序不再看到多次触摸

    我有一个应用程序 我在其中显示照片选择器 UIImagePickerController 但在用户关闭后 只有单次触摸才起作用 我想我知道问题的根源 但我不知道如何解决它 在显示模式对话框之前 触摸期间的堆栈如下所示 3 0x00074de
  • CakePHP 3 手动安装

    目前我使用 CakePHP 2 6 8 我想迁移到 CakePHP 3 安装指南 http book cakephp org 3 0 en installation html推荐使用Composer 但 Composer 并不能完成所有工作
  • 对数组中每个值的新 API 调用

    我基本上想要这种对象 id 5040 requester id 1234 requester name 约翰 id 5046 requester id 999 requester name 吉姆 id 5049 requester id 5
  • 使用CSS创建水平树

    我有一个数组 用于填充谱系表 它的顺序是这样的 3 1 4 5 2 6 等等 一个例子是http bullybloodlines net dogdetails php name muscletone 27s lucky bam bam of
  • 使用另一个类的 JavaFx 应用程序实例

    我有一个如下所示的 MainWindowFx 类 它基本上创建了一个简单的JavaFX GUI package drawappfx import javafx stage Stage import javafx scene Scene im
  • 流读(0)指令

    我在这里找到了一个代码https github com substack stream handbook https github com substack stream handbook它从流中读取 3 个字节 我不明白它是如何工作的 p
  • 背书政策不起作用

    当我使用管理员部署一个网络时 一个组织包括三个对等点 我的endorsement policy json如下 但不起作用 identities role name member mspId Org1MSP policy 1 of signe
  • 在网络上区分 iPadOS 和 macOS

    iPadOS beta 上 Safari 的用户代理目前与 macOS 上的 Safari 完全相同 还有其他方法可以区分 iPad 和 Mac 吗 iPad running iOS Mozilla 5 0 iPad CPU OS 10 3
  • Selenium WebDriver 有录像机吗?

    我正在 Selenium Webdriver 中使用 IE 特定应用程序 我知道我们可以截取执行的屏幕截图 同样 是否有任何选项可以将硒执行记录为视频 WebDriver 中什么也没有 酱料用途http www ffmpeg org htt
  • 如何让 AutoFixture AutoMoq 返回实例化对象中注入服务的结果?

    我正在尝试测试使用存储库服务的服务类 我设置了自定义设置 我认为应该与我的存储库服务一起使用 但返回默认的匿名结果 如果您查看下面的代码示例 当我调用 svc GetFoos 方法时 我试图获取在自定义类中注册的 Foo 对象 但我什么也没
  • POST_NOTIFICATIONS 权限对话框未显示

    我在询问新问题时面临不受欢迎的行为POST NOTIFICATIONS远程测试人员拥有的某些特定设备的权限 Pixel 4a Android 13 遗憾的是没有日志和 电缆访问 新安装的应用程序 仍然以 API30 为目标 但也尝试将目标设
  • Java、静态方法绑定和泛型都与一些方法重载结合在一起

    正如标题所暗示的 我的问题有点奇怪和复杂 我知道我要做的事情违反了 良好 编程实践的所有规则 但是嘿 如果我们不活一点 生活会怎样呢 所以我所做的是创建以下程序 请注意 这是一个更大的实验的一部分 旨在真正尝试和理解泛型 因此某些函数名称可