Search code examples
javaargumentsminimumvariadic-functions

Requiring at least one element in java variable argument list


In this code construct:

public MyClass(Integer... numbers) {
    do_something_with(numbers[]);
}

is it possible to require that numbers contains at least one entry in such a way, that this is checked at compile-time? (At run-time, of course, I can just check numbers.length.)

Clearly I could do this:

public MyClass(Integer number, Integer... more_numbers) {
    do_something_with(number, more_numbers[]);
}

but this isn't going to be very elegant.

The reason I would like to do this is to make sure that a sub-class does not simply forget to call this constructor at all, which will default to a call to super() with no numbers in the list. In this case, I would rather like to get the familiar error message: Implicit super constructor is undefined. Must explicitly invoke another constructor.

Could there be another way to achieve the same, like some @-annotation that marks this constructor as non-implicit?


Solution

  • I suppose one incredibly hacky way to do this is to create a no-args method and mark it as deprecated. Then compile with these two flags: -Xlint:deprecation -Werror. This will cause any use of a deprecated method to be a compile time error.

    edit (a long time after the initial answer):

    A less hacky solution would be to ditch the MyClass(Integer... numbers) constructor and replace it with MyClass(Integer[] numbers) (and add a private no-args constructor). It stops the compiler from being able to implicitly use the super class constructor, but without any args, and gives you a compile time error message.

    ./some_package/Child.java:7: error: constructor Parent in class Parent cannot be applied to given types;
        public Child(Integer[] args) {
                                     ^
      required: Integer[]
      found: no arguments
      reason: actual and formal argument lists differ in length
    

    The cost is your calling syntax will become a bit more verbose:

    new Child(new Integer[] {1, 2, 3});
    

    You can of course write a helper functions to help with this eg.

    public static Child newInstance(Integer... numbers) {
        return new Child(numbers);
    }
    
    @SafeVarargs
    public static <T> T[] array(T... items) {
        return items;
    }
    

    and then:

    Child c0 = Child.newInstance(1, 2, 3);
    Child c1 = new Child(array(1, 2, 3));