Java编译器选择错误的重载[重复]

2024-04-17

@Test
public void test() {
    MyProperties props = new MyProperties();
    props.setProperty("value", new Date());

    StringUtils.isNullOrEmpty(props.getProperty("value"));
}

public class MyProperties {
    private Map<String, Object> properties = new HashMap<String, Object>();

    public void setProperty(String name, Object value) {
        properties.put(name, value);
    }

    @SuppressWarnings("unchecked")
    public <T> T getProperty(String name) {
        return (T) properties.get(name);
    }
}

public class StringUtils {

    public static boolean isNullOrEmpty(Object string) {
        return isNullOrEmpty(valueOf(string));
    }

    public static String valueOf(Object string) {
        if (string == null) {
            return "";
        }
        return string.toString();
    }

    public static boolean isNullOrEmpty(String string) {
        if (string == null || string.length() == 0) {
            return false;
        }
        int strLength = string.length();
        for (int i = 0; i < strLength; i++) {
            char charAt = string.charAt(i);
            if (charAt > ' ') {
                return true;
            }
        }
        return false;
    }

}

多年来,这个单元测试一直通过。然后升级到 Java 8 后,在某些环境下,当通过 javac 编译代码时,它会选择 StringUtils.isNullOrEmpty(String) 重载。这会导致单元测试失败并显示以下错误消息:

java.lang.ClassCastException: java.util.Date cannot be cast to java.lang.String at com.foo.bar.StringUtils_UT.test(StringUtils_UT.java:35)

通过 ant(ant 1.9.6、jdk_8_u60、Windows 7 64 位)在我的机器上编译和运行时单元测试通过,但在具有相同版本的 ant 和 java 的另一台机器上失败(ant 1.9.6 jdk_8_u60、Ubuntu 12.04.4 32 位) 。

Java's 类型推断 https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html,它在编译时从所有适用的重载中选择最具体的重载,已在 Java 8 中进行了更改。我认为我的问题与此有关。

我知道编译器将 MyProperties.getProperty(...) 方法的返回类型视为 T,而不是 Date。由于编译器不知道 getProperty(...) 方法的返回类型,为什么它选择 StringUtils.isNullorEmpty(String) 而不是 StringUtils.isNullorEmpty(Object) - 后者应该始终有效?

这是 Java 中的错误还是只是 Java 8 类型推断更改的结果?另外,为什么使用相同版本的java的不同环境会以不同的方式编译此代码?


这段代码smells。是的,这在 Java 7 下通过了,是的,它在 Java 7 中运行正常,但是有一些东西绝对错误 here.

首先,我们来谈谈这个泛型类型。

@SuppressWarnings("unchecked")
public <T> T getProperty(String name) {
    return (T) properties.get(name);
}

你能一眼看出什么T should是?如果我在 Java 7 合规模式下使用 IntelliJ 在该行运行这些转换,我会得到这非常有帮助的信息ClassCastException:

Cannot cast java.util.Date to T

因此,这意味着在某种程度上,Java 知道这里有问题,但它选择更改该转换,而不是从(T) to (Object).

@SuppressWarnings("unchecked")
public <T> Object getProperty(String name) {
    return (Object) properties.get(name);
}

在这种情况下,演员阵容是多余的,你会得到一个Object正如您所期望的,从地图上可以看到。然后,调用正确的重载。

现在,在 Java 8 中,事情变得更加理智了;因为你并没有真正提供类型getProperty方法,它爆炸了,因为它really无法投射java.util.Date to T.


最后,我掩盖了要点:

泛型的这种使用是错误的并且是错误的。

你甚至不need这里泛型。您的代码可以处理String or an Object,而你的地图只包含Object无论如何。

你应该只返回Object来自getProperty方法,因为无论如何你只能从地图返回。

public Object getProperty(String name) {
    return properties.get(name);
}

这确实意味着您不再能够拨打电话directly进入签名为的方法String(因为你正在通过Object现在),但这确实意味着您损坏的泛型代码最终可以得到解决。


If you really不过,想要保留这种行为,您必须在函数中引入一个新参数,该参数实际上允许您指定想要从映射返回哪种类型的对象。

@SuppressWarnings("unchecked")
public <T> T getProperty(String name, Class<T> clazz) {
    return (T) properties.get(name);
}

然后你可以这样调用你的方法:

StringUtils.isNullOrEmpty(props.getProperty("value", Date.class));

现在我们绝对确定什么T是的,Java 8 对此代码很满意。这仍然有点气味,因为你把东西存放在一个Map<String, Object>;如果你有Object重写方法,您可以保证该映射中的所有对象都具有有意义的toString,那么我个人会避免使用上面的代码。

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

Java编译器选择错误的重载[重复] 的相关文章

随机推荐

  • 通过电子邮件询问评级

    在我的应用程序中 我们将通过向用户发送电子邮件来获取反馈 因此 如果用户填写字段并提交 那么我会将其存储在我的数据库中 我准备了一封 html 邮件 如下所示 但这里的问题是我无法在邮件中显示评级星星 这需要内联 css 但我无法将该 cs
  • AdMob 横幅不适用于移动数据

    我最近开始开发 Android 应用程序 当尝试在我的应用程序中加载横幅时 我在 logcat 中收到以下错误 03 02 17 30 58 509 I Ads 12181 Use AdRequest Builder addTestDevi
  • 给出源时,Django Rest Framework Serializer charfield 不更新

    我有一个带有选择 charfield 的模型字段 class Vehicle models Model name models CharField max length 100 STATUS CHOICES N New U Used P J
  • 如何在 Visual Studio Code 中更新 C# 版本?

    我已经安装了 C 扩展 我的 NET 框架已更新 但我无法修复它 我已经找到了修复方法 希望这对将来的人有所帮助 找你的 csproj file 将 LangVersion 属性更改为所需版本
  • AMD64 ABI 中的空类会发生什么情况?

    我正在看AMD64 ABI http www cs tufts edu comp 40 readings amd64 abi pdf并且它似乎没有指定如何传递空类类型 对于空类成员函数 似乎this照常传递 但对于空类 Clang 生成的代
  • RcppEigen - 从包中的内联函数到 .cpp 函数和“Map”

    一切似乎都在我的包中工作 但我想检查其步骤是否正确以及使用 Map 的内存使用情况 这是一个简单的示例 位于内联示例和fastLm 例子 这是一个内联函数 它取矩阵每一列的最大值 library Rcpp library inline li
  • 如何将 long (currentTimeInMillis) 转换为 UTC 时间戳?

    我的客户正在向我发送Long这可以被认为是 scala gt System currentTimeMillis res3 Long 1441056836609 scala gt 我如何将其转换为UTC时间戳 在服务器上 我们使用Java 8
  • Threejs + Vanilla JS 和 React-Three-Fiber + Create-React-App 之间的颜色差异

    这已经困扰我一段时间了 为什么 React Three Fiber 中的材质颜色看起来比 Threejs 中暗淡 两种实现中的对象及其属性是相同的 Threejs 版本相同 在新引导中实施 create react app 没有额外的依赖项
  • 在 Eclipse 中编辑代码时如何禁用粗体字体?

    我不希望 Eclipse 以粗体显示任何关键字 我浏览了 首选项 窗口中的很多选项 但找不到执行此操作的方法 是否可以 这取决于您正在编辑的文件类型 例如 在 Java 中 窗口菜单 gt 首选项 gt Jave gt 编辑器 gt 语法着
  • Java反应式框架的比较[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我看到许多框架 库声称它们可以帮助用 Java 构建响应式应用程序 例如 Akka Vert x RxJava Reactor QBit 等 他
  • 如何将 activemq-core.xsd url 与 jar 文件中找到的 activemq.xsd 关联?

    有人知道如何关联activemq core xsd网址与activemq xsd在 jar 文件 activemq core 5 2 0 jar 中找到 我在互联网上找到了一些解决方案 但没有成功 我收到此错误 Caused by org
  • 如何在 Rails 中保守 Secrets.yml 的秘密?

    我对 Rails 还很陌生 但我有一些 PHP 和其他语言编程经验 我真的很喜欢 Rails 并且正在为我的公司开发一个应用程序 但我仍然不完全理解 Secrets yml 文件如何与 git 和 heroku 配合使用 我知道秘密用于身份
  • Swiper 在 Jquery Mobile 中不起作用

    我在用idangero us 的 Swiper http www idangero us sliders swiper index php使用 Jquery 移动 我在这里使用滚动容器滑动器对于内容滑块 只是面临着将代码嵌入在一起的很多问题
  • rake db:rollback 为什么要回滚三步?

    通常当我这样做时 rake db rollback 它会回滚一次迁移 但现在 反复多次 已经回退了3步 怎么会有这种行为呢 我使用的是 Rails 3 2 13 您可以像这样指定步骤数 rake db rollback STEP 3 The
  • 如何更改Cloud Functions部署中使用的bundler版本?

    Context 从几天前开始 云功能的部署一直失败 Gemfile source https rubygems org ruby gt 2 7 0 git source github repo name https github com r
  • 如何在 Clojure 中处理大型二进制数据?

    如何在 Clojure 中处理大型二进制数据文件 我们假设数据 文件大约为 50MB 小到足以在内存中处理 但不是简单的实现 以下代码正确地从小文件中删除 M 但它会抛出OutOfMemoryError对于较大的文件 如 6MB defn
  • 需要在单个 Facebook 应用程序中添加多个域

    我正在尝试在我的 Facebook 应用程序中添加多个域 但尚未成功 我做了很多研究 发现 Facebook 不断改变其设置 添加多个域名最初是不可能的 但后来他们让它成为可能 然后又变得不可用 但没有更新如果现在可以完成的话 无论如何 我
  • 在 Windows 上安装 gokogiri 时找不到错误 libxml2

    我正在尝试安装gokogiri https github com moovweb gokogiri在 Windows 8 机器上按照 github 页面中的说明进行操作 安装 sudo apt get install libxml2 dev
  • ofstream::open 什么时候会失败?

    我正在尝试在 C 中尝试 捕获 抛出语句进行文件处理 并且我编写了一个虚拟代码来捕获所有错误 我的问题是为了检查我是否正确 我需要发生错误 现在我可以轻松检查infile fail 只需不在目录中创建所需名称的文件即可 但我怎样才能检查同样
  • Java编译器选择错误的重载[重复]

    这个问题在这里已经有答案了 Test public void test MyProperties props new MyProperties props setProperty value new Date StringUtils isN