“冗余强制转换为 java.lang.Object”警告,用于必要的强制转换

考虑这个最小的,可重复的例子

interface Code {
    static void main(String[] args) {
        symbol(
            String.valueOf(
                true ? 'a' :
                true ? 'b' :
                true ? 'c' :
                fail()
            )
        );
    }
    private static void symbol(String symbol) {
        System.out.println(symbol);
    }
    private static <R> R fail() {
        throw null;
    }
}

(接近最小值是有用的布尔表达式的代名词。我们可以忽略第一个之外(在实际代码中,有很多)。true? :

这“显然”给出了错误。

4: reference to valueOf is ambiguous
  both method valueOf(java.lang.Object) in java.lang.String and method valueOf(char) in java.lang.String match

好吧,让我们修复它。这是我想要的重载 - 我以后可能想添加:String.valueOf(Object)

            true ? "sss" :

(事实上,我之前确实有类似的东西,但现在已经删除了这个功能。

        String.valueOf((Object)(
            true ? 'a' :
            fail()
        ))

这将发出警告:

4: redundant cast to java.lang.Object

这是编译器警告或错误中的错误吗,我如何修复它以使代码合理并且没有警告或错误?

(编辑:我稍微改变了MRE。 来自模板。真正的代码确实使用文字字符*,而在其他地方,它使用重载,所以有问题(哦,Java!该代码避免全局状态,如 和 ,并且位于不同的类中。“开关”属于不可枚举类型。 是类似断言的方法的伴随,因此这就是为什么它在内部引发(未经检查的非空)异常的原因。throws ThrowableString.valueOfString.valueOf(char)toString()System.outsymbolfailfail

我实际修复它的方式是,不相关的是,我重新排列了代码,所以那里也有一些文字字符串。否则,我会使用毫无意义的等价物。我真正想知道的是:wtf?Object.class.cast(Object)

*实际上,真正的真实代码通过不同语言的词法分析器,该语言不区分文字字符,字符串,各种数字,布尔值,枚举等。为什么会这样呢?)


答案 1

自 Java 8 以来,有关“模棱两可的方法调用”的错误是正确的。

甚至在Java 8之前,你可以写

char c = fail();
Object o = fail();

没有编译器错误。当您将条件 like 传递给类似 的方法时,编译器会由于其有限的类型推断而推断和选取。condition? 'a': genericMethod()String.valueOf(…)<Object>fail()String.valueOf(Object)

但是Java 8引入了Poly表达式

独立表达式的类型可以完全从表达式的内容中确定;相反,poly 表达式的类型可能会受到表达式的目标类型的影响 (§5(转换和上下文))。

泛型方法的调用和包含多元表达式的条件(即泛型方法的调用)都是多元表达式。

因此,尝试调用在该条件条件下是有效的,正如我们可以推断的那样。请注意,这两种方法都不适用于严格的调用上下文,因为这两个变体都需要装箱或取消装箱操作。在松散的调用上下文中,两者都适用,并且都适用,因为我们是取消框后调用还是框文本 。String.valueOf(char)<Character>fail()String.valueOf(Object)String.valueOf(char)Characterfail()char'a'

由于 不是 的子类型,也不是 的子类型,因此方法和 更具体的方法都不是更具体,因此会生成编译器错误。charObjectObjectcharString.valueOf(Object)String.valueOf(char)


判断警告更加困难,因为没有正式的警告标准。在我看来,每个声称源代码工件已经过时的编译器警告,尽管代码在删除后不会做同样的事情(或者删除它甚至会引入错误),这是不正确的。有趣的是,在 Java 7 的 版本中确实已经存在警告,其中删除强制转换确实没有任何区别,所以也许,这是需要更新的剩余部分。javac


问题的解决方法取决于上下文,并且没有足够的相关信息。请注意,只需要一个分支不能分配给 ,以使该方法不适用。一旦您插入计算结果为 的分支,就会发生这种情况。您还可以使用来获得 Java 8 之前的编译器推断的相同类型。charString.valueOf(char)StringSurroundingClass.<Object>fail()

或者完全删除通用签名,因为这里不需要它。泛型方法似乎是在表达式上下文中使用抛出方法的变通方法。更简洁的解决方案是表达式的工厂方法,例如fail()

class Code {
    public static void main(String[] args) throws SpecificExceptionType {
        System.out.println(
            String.valueOf(switch(0) {
                case 0 -> 'a';
                case 1 -> 'b';
                case 2 -> 'c';
                default -> throw fail();
            })
        );
    }
    private static SpecificExceptionType fail() {
        return new SpecificExceptionType();
    }
    static class SpecificExceptionType extends Exception {
    }
}

如果切换表达式不可行,您可以使用

System.out.println(
    String.valueOf(
        true ? 'a' :
        true ? 'b' :
        true ? 'c' :
        Optional.empty().orElseThrow(Code::fail)
    )
);

两者的优点是具体说明可能引发的异常的实际类型,并且不需要诉诸未经检查的异常或声明。第二种方法可能会让人觉得很麻烦,但只不过是定义一个从不返回任何东西的通用方法。throws Throwable

当然,如果您只是接受更多代码的引入,还有其他可能性可以解决它,例如用于字符串转换的专用帮助器方法而没有重载,或者用于抛出方法的非泛型包装方法。或者一个临时变量或类型强制转换或泛型调用的显式类型,等等。此外,当使用 或 代替 时,表达式不是 poly 表达式,因此不会产生“模棱两可的方法调用”错误。"" + (expression)(expression).toString()String.valueOf(expression)

当然,由于这是一个错误的警告,你也可以保留强制转换并添加一个到方法中(并等到编译器开发人员修复这个问题)。@SuppressWarnings("cast")


答案 2

问题在于三元运算符的两个分支返回不同的类型。

怎么样:

    System.out.println(
        String.valueOf(
            true ? (Object)'a' : fail()
        )
    );

推荐