当前位置: 首页 > 面试题库 >

编译器的行为与通用方法的null参数不同

皇甫福
2023-03-14
问题内容

以下代码可以在Eclipse上完美编译,但是无法通过javac进行编译:

public class HowBizarre {
      public static <P extends Number, T extends P> void doIt(P value) {
      }

      public static void main(String[] args) {
            doIt(null);
      }
}

我简化了代码,所以现在根本不使用T。不过,我看不出发生此错误的原因。由于某种原因,javac决定T代表Object,然后抱怨Object不符合T的界限(这是对的):

HowBizarre.java:6:不兼容的类型;推断的类型实参java.lang.Number,java.lang.Object不符合类型变量P,T的范围

发现:<P,T>无效

必需:无效

       doIt(null);
           ^

请注意,如果我将null参数替换为非null值,则可以正常编译。

哪个编译器行为正确,为什么?这是其中之一的错误吗?


问题答案:

该问题是由于JLS规范所致,该规范要求必须将不可推论的类型参数推导为Object,即使它不满足界限(并因此会触发编译错误)。

以下是“错误”报告的摘录(为清楚起见,已对其进行了进一步注释):

[“错误” ID 6299211-方法类型变量:推断为空

](http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6299211)

该程序无法编译:

public class Try {
    void m() {
        java.util.Collections.max(null);
    }
}

状态 :已 关闭,不是缺陷。

评估这不是错误
。推理算法无法从参数(null)收集任何信息,并且不会在对返回值有任何期望的地方调用该方法。在这种情况下,编译器必须推断出java.lang.Object类型变量。

JLS
15.12.2.8推断未解决的类型参数

然后,还可以推断出尚未推断出的所有剩余类型变量 Object

但是,Object不是的子类型,Comparable<? super Object>因此不在声明的类型变量的范围内Collections.max

<T extends Object& Comparable<? super T>> T max(Collection<? extends T>)

进一步的探索

使用显式类型参数可以“解决”问题:

HowBizarre.<Number,Integer>doIt(null); // compiles fine in javac

为了表明与null参数无关,而与类型推断的绝对缺乏信息更多,可以尝试例如以下两种声明之一:

<T,U extends Comparable<T>> void doIt()

<T extends Number,U extends T> void doIt()

在这两种情况下,调用doIt();都不会在中进行编译javac,因为它必须推断U为符合Object15.12.2.8,即使这样做会触发编译错误。

关于Eclipse的注意

尽管以上代码片段均未​​在的某些版本中进行编译javac,但它们都在Eclipse的某些版本中进行了编译。这可能暗示了Eclipse的错误。众所周知,不同的编译器之间存在分歧。



 类似资料:
  • 问题内容: 在下面的代码中,输出为 串 如果我使用类型为参数的方法删除该方法,则输出为 目的 我知道当参数类型不完全匹配时方法的重载行为如何,但是我不明白如何将 null 视为和/或参数。 这有什么解释? 问题答案: 根据Java规范 15.12.2.5节,它始终使用最特定的方法。 简介相当具体: 如果多个成员方法既可访问又可应用于方法调用,则必须选择一个成员方法来为运行时方法分派提供描述符。Ja

  • 本章将介绍Rust编译器的参数。 Rust编译器程序的名字是rustc,使用它的方法很简单: $ rustc [OPTIONS] INPUT 其中,[OPTIONS]表示编译参数,而INPUT则表示输入文件。而编译参数有以下可选: -h, --help - 输出帮助信息到标准输出; --cfg SPEC - 传入自定义的条件编译参数,使用方法如 fn main() { if cfg!(he

  • 问题内容: 我正在编译glibc库。在我可以这样做之前,我需要先跑步。但是,要编译glibc,我需要使用gcc编译器,它不是计算机上的默认编译器。该手册说明以下内容。 现在,我的问题是我对该计算机没有任何管理权限。因此,如何使用不同于默认值的编译器。 问题答案: 在linux上,任何人都可以更改其进程的环境变量。不需要管理权限。 在bash中: 在csh中使用 在该命令之后在此shell中启动的任

  • 我是静态编程语言的新手,我发现静态编程语言不同于Java的特性之一是方法默认参数。https://kotlinlang.org/docs/functions.html#default-arguments 我正在创建一个简单的Kotlin应用程序,它将null值传递给具有2个不可为null的默认参数的方法,这会导致编译错误-类型不匹配:推断的类型是String?但应为字符串)。 一种解决方法是将方法

  • 问题内容: 让我们首先考虑一个简单的场景(请参阅ideone.com上的完整源代码): 这两个通配符是无关的,这就是为什么你可以doNothing使用和a 进行调用的原因。换句话说,两者?可以指完全不同的类型。因此,以下内容无法编译,这是可以预期的(同样在ideone.com上): 到目前为止,一切都很好,但是这里的事情开始变得非常混乱(如ideone.com所示): 上面的代码可以在Eclips

  • 问题内容: 因此,如果我有一个,由于某种原因,如果我在另一段代码(如)中引用它,则在编译过程中它不会内联到代码中。因此,它不是在被编译之后而是。 问题答案: 您不是编译时间常数,因为JLS 表示 不是。只能在常量表达式中使用的类型是基本类型和。 它的意义是,一个实例(通常)具有语义上重要的对象标识,该标识将其与其他实例区分开。此对象标识不能编码在类文件中……至少,不能用当前的类文件格式编码。(如果