Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Find out whether function was called with varargs or array

Is there a way to find out whether a Java function (or a constructor) that takes varargs was actually called with varargs or with an array?

Say I have the following:

public class MyCompositeObjects {

    MyObject[] objects;

    MyCompositeObjects(MyObjects... objects) {
        this.objects = Arrays.copyOf(objects,objects.length);
        // or just: this.objects = objects; ?
    }

    // ...
}

The constructor may be called with a single MyObject[] argument, which may change later, and if I do not copy the array in the constructor those changes will apply to the member variable objects as well, right? However, if the constructor is called with several MyObjects, there is no other reference to the array* to change it later outside the class, so I could assign it directly. Can I tell inside the constructor (or, generally, any function that takes varargs) how it was called?

*nb: Is there a specific name for this? Is it simply an anonymous array?

like image 207
arne.b Avatar asked Mar 25 '11 14:03

arne.b


2 Answers

No, you can't. It's meant to be entirely transparent - this code:

new MyCompositeObjects(a, b);

is exactly equivalent to

new MyCompositeObjects(new MyObjects[] { a, b });

If you can trust your callers to do the right thing, you could always create two static methods and make the constructor private:

public static MyCompositeObjects createWithCopy(MyObjects[] values) {
    return new MyCompositeObjects(Arrays.copyOf(values, values.length));
}

public static MyCompositeObjects createWithoutCopy(MyObjects... values) {
    return new MyCompositeObjects(values);
}

private MyCompositeObjects(MyObjects[] values) {
    this.objects = values;
}

Note how the "with copy" version doesn't use varargs, which should help users to use the right version.

like image 99
Jon Skeet Avatar answered Sep 19 '22 16:09

Jon Skeet


The only way to know is to parse the code. This is because varargs is basicaly a compile time feature and doesn't change how the program runs.

If in doubt, I would always copy the array. Unless you know this will be a performance issue.

BTW: You can do the following.

MyCompositeObjects(MyObjects o1, MyObjects... objects) {

MyCompositeObjects(MyObjects[] objects) {

However, this likely to do the opposite of what you want.

Another option is to use a static factory.

private MyCompositeObjects(MyObjects[] objects) {
   this.objects = objects;
}

MyCompositeObjects create(MyObjects... objects) {
    return new MyCompositeObjects(objects.close());
}

MyCompositeObjects createNotCopied(MyObjects... objects) {
    return new MyCompositeObjects(objects, false);
}

Use the more cumbersome method name for the less safe version. This means if a method is chosen without much thought, the safe version is more likely to be used.

like image 25
Peter Lawrey Avatar answered Sep 23 '22 16:09

Peter Lawrey