Between 中的可变参数列表作为具有不同数据类型的参数

Variable Argument List in Between as a Parameter with different data type

我在使用可变参数时了解到:-

public void myMethod(String... args, int val){}

The variable argument type String of the method myMethod must be the last parameter.

如果两者都像String,它给出的错误是相当大的,但在这种情况下,我将int设置为第二个参数,因此在运行时JVM可以检查参数类型 & 可以区分为:-

myMethod("HI", "HELLO", 9)

那不可行吗。我还遗漏了任何其他会产生错误的点吗?

错误信息正确。如果你使用可变参数,它必须是最后一个参数。你可以说它可以解决问题,但它不会。只需交换参数的顺序即可。

当您在随机位置使用可变参数重载方法时,消除歧义将成为一场噩梦...(更不用说多个可变参数)。

这是在 JLS 中指定的,section 8.4.1。语言规范要求将此作为形式参数列表的一部分:

FormalParameterList:
    ReceiverParameter 
    FormalParameters , LastFormalParameter 
    LastFormalParameter

LastFormalParameter 标记是可变参数定义和允许的地方。

LastFormalParameter:
    {VariableModifier} UnannType {Annotation} ... VariableDeclaratorId 
    FormalParameter

关注...。这是形式参数语法中唯一允许这样做的地方。 FormalParameters 没有这个余地。

FormalParameters:
    FormalParameter {, FormalParameter} 
    ReceiverParameter {, FormalParameter}

语言设计者选择禁止这样做的原因有很多:

  1. 区分必须由编译器完成,因为可变参数完全是编译器的一个特性。它们只是转换为隐式数组构造函数。
  2. 您的示例在理论上可行,但限制会非常严格:例如,编译器很难在这种情况下看到第一个可变参数参数的结束位置

    void foo(Object... objs, String... s)
    foo("a", "b", "c")
    

    另一个例子是这样的:

    void bar(int... ints, long... longs)
    foo(1, 2, 3, 4)
    

    您可能会争辩说 intlong 是不同的数据类型,但不幸的是,由于扩大转换,可以在预期 long 的地方使用整数。另一个例子涉及拳击:

    void baz(Object... objs, int... ints)
    baz(1, 2, 3, 4)
    

    intObject没有直接关系,但是int可以转化为Integer,是Object的子类。

  3. 重载的方法和可变参数越多,情况就变得越复杂。

  4. 有点技术性,但仍然相关:在字节码中,varargs不是参数属性,而是方法修饰符标志(ACC_VARARGS)。这意味着方法要么是可变的(最后一个参数是可变参数)要么不是。
  5. 如果您确实需要在您的方法中使用可变参数,请将其移至最后一个位置。唯一不能这样做的情况是当你有多个可变参数时,这是不可能的。
  6. 如果编译器允许您在声明端执行此操作而不会出错,那么在使用端几乎不可能出现有用的错误。
  7. String...String[] 相同,只是您不需要在使用位置创建数组。你可以声明一个方法

    void foo(String[] strings, int i)
    

    并将其命名为

    void foo({ "a", "b" }, 2)
    

    只需再敲击 2 次按键,并且没有可变参数参数引入的所有麻烦。

我已经在 JVM-language 编译器上工作了一年多了,我也考虑过添加这个功能。但是,方法解析系统 已经 极其复杂(我决定包括自定义 infixprefix 运算符/方法以及命名和默认参数并没有使它更容易),并且多个可变参数不会使它更容易。