【问题标题】:Multiple constructors with variable argument parameters具有可变参数参数的多个构造函数
【发布时间】:2019-06-04 15:48:06
【问题描述】:

给定这两个构造函数:

SomeClass(int... params)
{
   // Do things
}

SomeClass(long... otherParams)
{
   // Do other things
}

当对象 foo 被实例化时会发生什么?

SomeClass foo = new SomeClass();

是否以某种方式调用了未定义的默认构造函数?还是调用了具有空数组的那些构造函数之一?如果有,有什么先例?

我做了一些基本的测试,发现如果定义了一个没有参数的构造函数,那么它将被调用。否则,它似乎调用了一个模棱两可的。

【问题讨论】:

  • 只有完全没有显式构造函数的类才能获得默认构造函数。您的类没有默认构造函数。
  • @Andreas 通过进一步的测试(使用原始类型),似乎调用了具有最低字节大小的类型。不过,这纯粹是推测性的,我想要一个明确的答案。
  • “通过进一步的测试(使用原始类型),似乎调用了具有最低字节大小的类型。” - 添加下面的构造函数,你会发现它不会编译:SomeClass(boolean... moreParams)

标签: java class constructor variable-length-array


【解决方案1】:

根据this very good answer in "Varargs in method overloading in Java" question,下面是Java 编译器用于选择要调用的方法签名的规则。它们基于JLS 5.3. Method Invocation Conversion docs。

  1. 基本扩展使用可能的最小方法参数
  2. 包装器类型不能扩展到另一个包装器类型
  3. 您可以从 int 框到 Integer 并扩大到 Object 但不能到 Long
  4. 加宽胜过拳击,拳击胜过 Var-args。
  5. 您可以先装箱然后加宽(int 可以通过 Integer 变为 Object
  6. 你不能先加宽然后框(int 不能变成 Long
  7. 您不能将 var-args 与加宽或装箱结合使用

因为两个构造函数都是 var-args(规则 7),编译器将回退到其他规则并选择使用最小类型的方法(规则 1)。

您可以使用以下代码确认此行为:

static class SomeClass {
  SomeClass(long... value) { System.out.println("Long"); }
  SomeClass(int... value) { System.out.println("Int"); }
  SomeClass(byte... value) { System.out.println("Byte"); }
}

public static void main(String[] args) throws Exception {
  SomeClass o = new SomeClass(); // Byte
}

规则 1 中使用的基元类型之间的精确子类型关系在 JLS 4.10.1. Subtyping among Primitive Types 中进行了解释。

以下规则定义了原始类型之间的直接超类型关系:

  • 双精度>1浮点数

  • float >1 long

  • long >1 int

  • int >1 char

  • int >1

  • 短>1字节

【讨论】:

    【解决方案2】:

    只有完全没有任何显式构造函数的类才能获得默认构造函数。对于一个明确定义了一个或多个构造函数的类,它们的数量,变量与否,都没有影响。因此,一个类没有空构造函数是相当普遍的,实际上你的类就是这种情况。

    从多个可用构造函数中进行选择与在重载方法中进行选择的方式相同。首先,确定可用 构造函数。然后,识别那些适用给定参数的参数。最后,选择适用的构造函数中最具体的。 JLS10 第 15.12 节规定了详细信息。如果该过程未导致准确识别一个构造函数,则这是一个编译时错误。

    在您的示例中,两个可用的构造函数都适用于空参数列表,因此归结为选择最具体的一个问题。 JLS 提供了一个非正式的描述:

    如果任何调用由 第一种方法可以在没有 编译时错误。

    形式规则围绕形式参数的类型展开,并考虑原始类型之间的形式类型/子类型关系,最终结果是SomeClass(int...)SomeClass(long...) 在两者都适用时更具体。那么,在您的示例中选择了前者。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多