13

在我的代码中,当类型是数组时,在实现泛型方法时使用可变参数似乎很方便:

public interface Codec<D,E> {
  E encode(D decoded);
  D decode(E encoded);
}

public class MyCodec implements Codec<byte[], char[]> {
  @Override char[] encode(byte... decoded) {...}
  @Override byte[] decode(char... encoded) {...}
}

当我写这篇文章时,Eclipse 会显示一个警告:

与 MyCodec.encode(byte...) 和 Codec.encode(byte[]) 不同,可变参数方法只能覆盖或被其他可变参数方法覆盖

我应该忽略警告,还是会导致一些无法预料的问题?

4

3 回答 3

9

这是一个特定于 Eclipse 的警告。它与泛型无关,可以用这个例子重现:

class A {
    m(int[] ints) { }
}

class B extends A {
    @Override
    m(int... ints) { }
}

正如其他答案所指出的那样,可变参数纯粹是编译时功能,在运行时没有区别。我尝试搜索警告背后的具体原因,但无法解决任何问题。在可变参数和非可变参数之间交替方法覆盖可能被认为是不好的做法,因为它令人困惑和随意。但这通常是- 只要调用者总是使用静态类型MyCodec而不是编码来与Codec<byte[], char[]>.

不幸的是,没有办法抑制这个警告——甚至@SuppressWarnings("all")不会让它屈服。考虑到它是多么模糊的警告,这是不幸的。这是关于同一问题的古老对话:http: //echelog.com/logs/browse/eclipse/1196982000(滚动到 20:45:02) - 证明它早在你之前就有人了。似乎是一个无法抑制的 Eclipse 错误。

于 2013-06-07T04:20:27.510 回答
4

我写了两个测试文件。这是第一个:

public class Test {
    public static void main(String... args) {
        System.out.println(java.util.Arrays.toString(args));
    }
}

这是第二个:

public class Test {
    public static void main(String[] args) {
        System.out.println(java.util.Arrays.toString(args));
    }
}

(这两个文件之间的唯一区别是String[] argsvs String... args。)

然后,我javap -c在每个文件上运行以查看反汇编。该main方法的内容是相同的:

Code:
   0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
   3: aload_0       
   4: invokestatic  #3                  // Method java/util/Arrays.toString:([Ljava/lang/Object;)Ljava/lang/String;
   7: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
  10: return    

唯一的区别是方法头,它只是每个方法的方法签名:

  • public static void main(java.lang.String[]);
  • public static void main(java.lang.String...);

考虑到这一点,我会说这是一个安全的假设,即不会发生任何不好的事情。

于 2013-06-07T00:55:16.733 回答
1

根据字节码,没问题。

public byte[] encode(char...);
  flags: ACC_PUBLIC, ACC_VARARGS

  LineNumberTable:
    line 4: 0
  LocalVariableTable:
    Start  Length  Slot  Name   Signature
           0       2     0  this   LMyEncoder;
           0       2     1  args   [C          // char[]
  Code:
    stack=1, locals=2, args_size=2
       0: aconst_null   
       1: areturn                              // return null;
    LineNumberTable:
      line 4: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
             0       2     0  this   LMyEncoder;
             0       2     1  args   [C

public java.lang.Object encode(java.lang.Object);
  flags: ACC_PUBLIC, ACC_BRIDGE, ACC_VARARGS, ACC_SYNTHETIC

  LineNumberTable:
    line 1: 0
  LocalVariableTable:
    Start  Length  Slot  Name   Signature
  Code:
    stack=2, locals=2, args_size=2
       0: aload_0       
       1: aload_1       
       2: checkcast     #27          // class "[C"            -> char[]
       5: invokevirtual #28          // Method encode:([C)[B  -> return byte[]
       8: areturn       
    LineNumberTable:
      line 1: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature

如果您使用接口的一个引用进行调用,则带有标志的方法ACC_BRIDGE检查(checkcast)如果参数的类型与类型参数中定义的相同(否则,但如果您始终提供类型参数java.lang.ClassCastException,则永远不会发生),然后运行方法实现。

另一方面,如果您使用 javac 编译它,则不会显示任何警告。

于 2013-06-07T01:00:47.473 回答