Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

implementing a generic method with varargs

In my code, it appears convenient to use varargs when implementing a generic method when the type is an array:

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) {...}
}

When I write this, Eclipse shows a warning:

Varargs methods should only override or be overridden by other varargs methods unlike MyCodec.encode(byte...) and Codec.encode(byte[])

Should I just ignore the warning, or is this going to cause some unforeseen problems?

like image 662
ykaganovich Avatar asked Jun 06 '13 23:06

ykaganovich


3 Answers

This is an Eclipse-specific warning. It has nothing to do with generics specifically and can be reproduced with this example:

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

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

As the other answers point out, varargs are purely a compile-time feature and there's no difference at runtime. I tried searching for the specific reasoning behind the warning but couldn't turn anything up. Likely it's considered bad practice to alternate method overrides between varargs and non-varargs because it's confusing and arbitrary. But this is in general - your use case seems more reasonable as long as callers are always going to be using a statically-typed MyCodec instead of coding to interface with a Codec<byte[], char[]>.

Unfortunately there is no way to suppress this warning - even @SuppressWarnings("all") won't make it yield. Which is unfortunate considering how obscure of a warning it is. Here's an ancient conversation about this same issue: http://echelog.com/logs/browse/eclipse/1196982000 (scroll to 20:45:02) - proving it's bit people long before you. Seems like an Eclipse bug that it can't be suppressed.

like image 194
Paul Bellora Avatar answered Oct 15 '22 22:10

Paul Bellora


I wrote two test files. Here's the first:

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

And here's the second:

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

(The only difference between these two files is the String[] args vs String... args.)

Then, I ran javap -c on each file to see the disassembly. The contents of the main method were identical:

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    

The only difference was the method header, which was simply the method signature of each method:

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

With this in mind, I would say that it's a safe assumption that nothing bad will happen.

like image 35
wchargin Avatar answered Oct 15 '22 22:10

wchargin


According to the bytecode, no problem.

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

If you make a call using one reference of the interface, the method with the flag ACC_BRIDGE check (checkcast) if the type of the argument is the same as is defined in the type parameter (java.lang.ClassCastException otherwise, but will never happen if you allways provides the type parameter), then run the method implementation.

In another hand, if you compile this with javac, none warning is show.

like image 34
Paul Vargas Avatar answered Oct 15 '22 22:10

Paul Vargas