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、静态方法绑定和泛型都与一些方法重载结合在一起 的相关文章

  • 如何知道 HTTP 服务器何时完成发送数据

    我正在开发一个面向浏览器 代理的项目 我需要下载网页 向 Web 服务器发送自定义 HTTP 请求后 我开始监听服务器响应 读取响应时 我检查响应标头中的 Content Length row 如果我得到其中之一 很容易确定服务器何时完成发
  • HibernateTemplate 可以与 EntityManager 共存吗?

    我们有一个 spring 3 应用程序 它仍然使用已弃用的HibernateTemplate为了持久性并希望迁移到更现代的 JPAEntityManager 是否可以在迁移过程中并行使用这两个 API 甚至可能在单个事务中同时使用 以便我们
  • 为 JSP 创建注销链接?

    当用户登录我的应用程序时 他提交一个要通过 Servlet 处理的表单 servlet 为用户创建一个会话 我如何创建一个链接以便用户可以注销 我似乎无法直接链接到 Servlet 如何删除会话并链接回主页 HttpSession sess
  • Javadoc 链接到其他类中的方法

    目前我正在使用以下 Javadoc 语法引用其他类中的方法 see link com my package Class method 据我从文档中了解到 这是执行此操作的正确方法 但现在到了有趣的部分 或者说令人沮丧的部分 当我生成这个 j
  • 在 libgdx 中批处理多维数据集时出现问题

    我正在尝试开发一款游戏 在屏幕上渲染多达 300 个立方体 为每个多维数据集创建新的 modelInstance 时 modelBatch 的性能非常糟糕 据我所知 没有 3d 批处理可以将所有立方体批处理到一次绘制调用 所以我拼命地尝试以
  • 浏览时 Java Applet 不会被终止

    当用户离开加载小程序的页面时 如何停止 Java 小程序的进程 我正在使用 Chrome 现在要杀死小程序 我必须使用窗口的任务栏并杀死进程 java exe Java applet 具有生命周期方法 那些是init start stop
  • 为什么用java日历解析时会得到错误的月份

    Date fakeDate sdf parse 15 07 2013 11 00 AM Calendar calendar Calendar getInstance calendar setTime fakeDate int current
  • android新手需要了解“?android:attr/actionBarSize”

    我正在经历拉尔斯 沃格尔的教程 http www vogella com articles AndroidFragments article html在使用 Fragments 时 我遇到了以下代码 android layout margi
  • java中的散列是如何工作的?

    我正在尝试弄清楚java中的哈希值 例如 如果我想在哈希图中存储一些数据 它是否会有某种带有哈希值的底层哈希表 或者 如果有人能够对哈希的工作原理给出一个很好且简单的解释 我将非常感激 HashMap 基本上在内部实现为数组Entry 如果
  • 获取文本文件中行的字节偏移量?

    我有一个文本文件 例如 one two three four five 我需要获取文件中每一行的偏移量 我如何在 Java 中做到这一点 我搜索了一些 I O 库 如 BufferedReader 和 RandomAccessFile 但我
  • rmi类找不到异常

    我使用 java rmi 编写了一个简单的项目并导出到可执行 jar 文件 当我尝试运行它时 有时会出现异常 有时会起作用 当我指定 Djava rmi server codebase file serverClasses 时 它似乎没有正
  • 如何用 Java 制作 Windows 7 工具提示

    我一直在网上到处寻找 但没有找到这个小问题的答案 在 Windows 7 中 我认为在 Vista 中 您有一个漂亮的圆形银色工具提示 它看起来比旧的黄色盒装蹩脚工具提示要好得多 下面的 How do I make a Windows 7
  • 使用 PowerMock 和 TestNG 模拟单个静态方法

    class StaticClass public static String a return a public static String ab return a b 我想嘲笑StaticClass a以便它返回 x 并致电StaticC
  • 不带破折号的 CliBuilder 参数

    使用 Groovy CliBuilder 理想情况下我希望有一个命令行 如下所示 MyProgram groovy CommandName arg1 arg2 arg3 是否可以使用 CliBuilder 解析提取 CommandName
  • Thread.interrupt() 和 Thread.interrupted() 到底是如何工作的? [复制]

    这个问题在这里已经有答案了 从设置线程状态的角度来看 我不清楚这两种方法 Java 文档说 Thread interrupt 设置线程中断状态标志 调用 Thread interrupted 方法给出线程的状态并清除该标志 当这在实际场景中
  • 枚举

    我试图拥有一组扩展通用接口的枚举 例如 interface Fooable void someCommonMethod enum E1 implements Fooable some enumuerations and a definiti
  • 让 subclipse 在 Ubuntu 64 和 Indigo 上工作 - 加载了不兼容的 JavaHL 库。需要 1.7.x 或更高版本

    我该如何解决 我在 ubuntu 64 上使用 Eclipse indigo 我安装了http subclipse tigris org update 1 8 x http subclipse tigris org update 1 8 x
  • 确保 MAVEN_HOME 设置正确

    这里是 Java 和 Maven 菜鸟 使用 OSX 10 8 并使用 HomeBrew 安装 Maven 1 如果我说which mvn我会得到这个 usr local bin mvn 2 如果我说echo MAVEN HOME我不会得到
  • 在android中测量不规则多边形的面积

    我正在开发一个应用程序 在其中我在地图上绘制多边形 并且我使用的地图不是谷歌 它的Mapsforge开源离线地图库 我可以通过将地理点转换为像素点来轻松在地图上绘制多边形 但在这里我想发现是不规则的多边形 为此我做了很多尝试 但它让我失败了
  • Java编程编译jar

    我有一个文本文件中的java源代码 必须在源代码中输入一些自定义的硬编码变量 然后将其转换为 jar 这是可行的 但是当我运行 jar 时 找不到 Main 类 当我用 WinRAR 解压 jar 文件时 我似乎找不到错误 当我通过 cmd

随机推荐

  • 重新打包 .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、静态方法绑定和泛型都与一些方法重载结合在一起

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