【问题标题】:Java type promotion in parameters参数中的 Java 类型提升
【发布时间】:2023-03-18 11:22:02
【问题描述】:

我偶然发现了这个 sn-p:

public class ParamTest {
    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

这将导致编译错误:

Error:(15, 9) java: 对 printSum 的引用不明确,ParamTest 中的方法 printSum(int,double) 和 ParamTest 中的方法 printSum(long,long) 匹配

这怎么模棱两可?由于第一个参数已经是 int,在这种情况下不应该只提升第二个参数吗?在这种情况下不需要提升第一个参数对吗?

如果我更新代码添加另一个方法,编译成功:

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

让我扩展只是为了澄清。下面的代码导致歧义:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

那么下面的这段代码会导致歧义:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

但是这个不会导致歧义:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, double b) {
        System.out.println("In longDBL " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

【问题讨论】:

  • 编译器可以映射你的调用 printSum(1, 2);到 printSum(long a, long b) 或 printSum(int a, double b) 因此它是模棱两可的。您必须明确地帮助编译器进行选择,具体方式如下: printSum(1, 2d)
  • 你错误地引用了错误信息,区别很重要。实际的错误消息是:Error:(15, 9) java: reference to printSum is ambiguous both method printSum(int,double) in ParamTest and method printSum(long,long) in ParamTest match - 这不是模棱两可的方法,而是对方法的调用模棱两可。
  • @ErwinBolwidt 错误消息来自 Eclipse 抱歉,我在此处引用错误。无论如何,我仍然不明白,因为添加 printSum(int a, long b) 会删除错误消息。
  • JLS-5.3 => 如果表达式的类型不能通过松散调用上下文中允许的转换转换为参数的类型,则会发生编译时错误。 似乎适用于上下文,但很难直接推断出如何。 +1
  • 我们绝对需要一个规范的问题:stackoverflow.com/…。"

标签: java java-8 type-promotion


【解决方案1】:

因为 int 值在 java 中也可以被认为是 double。意味着double a = 3 是有效的并且与长long b = 3 相同所以这就是它造成歧义的原因。 你打电话给

printSum(1, 2);

对所有三种方法都感到困惑,因为这三种方法都是有效的:

int a = 1;
double b =1;
long c = 1;

您可以将 L 放在末尾,以指定它是长值。例如:

printSum(1L, 2L);

对于 double 你需要转换它:

printSum((double)1, 2L);

还阅读了@Erwin Bolwidt 的评论

【讨论】:

  • 是的,编译器对所有 3 种方法都感到困惑,首先编译器查找确切的类型,如果没有找到,则尝试查找兼容的类型,在这种情况下,所有方法都是兼容的。跨度>
  • 如果我有 4 个方法声明而不是 3 个,歧义就会消失: public static void printSum(int a, double b) public static void printSum(int a, long b) public static void printSum(long a, long b) public static void printSum(double a, long b) 所以虽然我同意你的回答,但它仍然没有回答这个问题。如果我没记错的话,如果确切的类型不可用,Java 会自动进行类型提升。我只是不明白为什么它与那些 3 变得模棱两可。
【解决方案2】:

我认为这与 JLS 关于15.12.2.5. Choosing the Most Specific Method 的特定规则有关。它指出:

如果多个成员方法既可访问又适用于方法调用,则有必要选择一个为运行时方法分派提供描述符。 Java 编程语言使用选择最具体方法的规则。

Java如何选择最具体的方法在正文中进一步解释:

非正式的直觉是,如果第一个方法处理的任何调用都可以传递给另一个方法而不会出现编译时错误,那么一个方法比另一个方法更具体。在诸如显式类型化的 lambda 表达式参数(第 15.27.1 节)或变量 arity 调用(第 15.12.2.4 节)之类的情况下,允许有一定的灵活性来适应另一个签名。

在您的示例中,所有方法都可以访问并适用于方法调用,因此,Java 需要确定其中哪些是最具体的

对于这些方法,没有一个更具体:

public static void printSum(int a, double b) {
    System.out.println("In intDBL " + (a + b));
} // int, double cannot be passed to long, long or double, long without error

public static void printSum(long a, long b) {
    System.out.println("In long " + (a + b));
} // long , long cannot be passed to int, double or double, long without error

public static void printSum(double a, long b) {
    System.out.println("In doubleLONG " + (a + b));
} // double, long cannot be passed to int, double or long, long without error

第四种方法正是因为它满足了最具体的必要条件,从而消除了歧义。

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

也就是说,(int, long) 可以传递给 (int, double)、(long, long) 或 (double, long) 而不会出现编译错误。

【讨论】:

  • 所以总结一下,如果所有方法都可以通过调用访问,那么 java 会识别该方法,可以调用其他方法而不会出现编译时错误,如果发现则使用最具体的方法并调用它否则歧义错误?我认为,这是一个有效的答案@riruzen。
  • 谢谢!我用 (double, int) vs (double, long) 尝试了这个,它确实消除了歧义,然后 (double, int) vs (long, double) 正如预期的那样模棱两可。
【解决方案3】:

这确实是一个非常有趣的问题。让我们一步一步地了解 Java 语言规范。

  1. 当编译器试图识别可能适用的方法时,它首先要做的是serching for methods applicable by Strict Invocation

  2. 在你的情况下没有这样的方法,所以下一步是find methods applicable by Loose Invocation

  3. 此时所有方法都匹配,因此在松散调用适用的方法中选择最具体的方法 (§15.12.2.5)。

    李>

这是一个关键时刻,所以让我们仔细看看。

一种适用的方法 m1 比另一种适用的方法更具体 方法 m2,用于使用参数表达式 e1、...、ek、if 的调用 以下任何一项都是正确的:

(我们只对以下情况感兴趣):

  • m2 不是泛型的,m1 和 m2 可以通过严格或松散的调用来应用,其中 m1 有形参类型 S1,...,Sn 和 m2 有形参类型 T1,...,Tn,类型对于所有 i (1 ≤ i ≤ n, n = k),对于自变量 ei,Si 比 Ti 更具体。

简单地说,如果其所有参数类型都更具体,则方法更具体。还有

对于任何表达式,如果 S <: t href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.10" rel="nofollow" target="_blank">§4.10),类型 S 比类型 T 更具体。

表达式S &lt;: T 表示ST 的子类型。对于原语,我们有以下关系:

double > float > long > int

让我们看看你的方法,看看哪一种比其他方法更具体。

public static void printSum(int a, double b) {  // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(double a, long b) { // method 2
    System.out.println("In doubleLONG " + (a + b));
}

在这个例子中,方法 1 的第一个参数显然比方法 2 的第一个参数更具体(如果你用整数值调用它们:printSum(1, 2))。 但第二个参数对方法 2 更具体,因为long &lt; double。所以这些方法都没有比另一种更具体。这就是为什么你在这里有歧义。

在以下示例中:

public static void printSum(int a, double b) { // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(long a, double b) { // method 2
    System.out.println("In longDBL " + (a + b));
}

方法一的第一个参数类型比方法二的参数类型更具体,因为int &lt; long和第二个参数类型对两者都是一样的,所以选择了方法一。

【讨论】:

  • 我没有否决你的答案,但这个答案并没有解释为什么 (int, double) 与 (long, long) 不明确,因为显然 (int, double) 应该更具体第一个参数。
  • @riruzen 它确实解释了:double 并不比long 更具体。对于要选择的方法,所有类型参数必须更具体:对于所有 i (1 ≤ i ≤ n, n = k) 的参数 ei,类型 Si 比 Ti 更具体
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-30
  • 1970-01-01
  • 2011-12-28
  • 1970-01-01
  • 1970-01-01
  • 2011-12-18
  • 2017-11-09
相关资源
最近更新 更多