Реализация универсального метода с varargs

В моем коде удобно использовать varargs при реализации универсального метода, когда тип является массивом:

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 показывает предупреждение:

Методы Varargs должны переопределяться или переопределяться другими varargs в отличие от MyCodec.encode(байт...) и Codec.encode(байт [])

Должен ли я просто игнорировать предупреждение, или это вызовет некоторые непредвиденные проблемы?

Ответ 1

Это специальное предупреждение для Eclipse. Он не имеет ничего общего с дженериками и может быть воспроизведен с помощью этого примера:

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

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

Как указывают другие ответы, varargs - это просто функция времени компиляции, и во время выполнения нет никакой разницы. Я попытался найти конкретные аргументы за предупреждением, но ничего не мог поделать. Вероятно, он считал, что неправильная практика чередует переопределения методов между varargs и non-varargs, потому что это запутанно и произвольно. Но это в целом - ваш пример использования кажется более разумным, если вызывающие абоненты всегда будут использовать статически типизированный MyCodec вместо кодирования для взаимодействия с Codec<byte[], char[]>.

К сожалению, нет способа подавить это предупреждение - даже @SuppressWarnings("all") не даст результата. Что печально, учитывая, насколько неясным является предупреждение. Здесь древний разговор по этому же вопросу: http://echelog.com/logs/browse/eclipse/1196982000 (прокрутите до 20:45:02) - доказав, что это бит, люди задолго до вас. Кажется, что ошибка Eclipse не может быть подавлена.

Ответ 2

Я написал два тестовых файла. Здесь первое:

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[] args vs 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...);

С учетом этого я бы сказал, что это безопасное предположение, что ничего плохого не произойдет.

Ответ 3

В соответствии с байт-кодом нет проблем.

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 check (checkcast), если тип аргумента такой же, как определено в параметре типа (java.lang.ClassCastException иначе, но никогда не произойдет, если вы всегда предоставляете , затем запустите реализацию метода.

В другой руке, если вы скомпилируете это с помощью javac, ни одно предупреждение не отображается.