4

Is there any method, other than through static factory methods or builder patterns, to handle default constructor parameters of type array?

There is plenty of fruitful discussion here regarding how to do this with builder patterns and static factory methods, and there are plenty of other examples out there of how to handle various constructors of varying parameter count, but all these patterns appear to include only parameters of simple type (e.g. int, double, etc.), which I have no problem getting to work. I am trying to have a default parameter of type array, and am unable to achieve this with the standard constructor setup.

It tried a few approaches to this and they all result in errors of some kind, so I am searching for an alternative, still making use of a constructor pattern. The first approach:

public class MyClass{

    double[] mWeights;

    public MyClass(){
        double[] weights = {1, 1, 1}
        this(weights);
    }

    public MyClass(double[] weights){
        this.mWeights = weights
    }
}

but this results in an error:

Call to 'this()' must be first statement in constructor body

Alternatively, I tried:

public class MyClass{

    double[] mWeights = new double[] {1, 1, 1};  

    public ActionDistribution(){
        this(mWeights); 
    }  

    public ActionDistribution(double[] weights){  
        this.mWeights = weights;
    }
}

but this results in an error:

Cannot reference 'MyClass.mWeights' before supertype constructor has been called.

Lastly, I tried:

public class MyClass{

    double[] Mweights;  

    public ActionDistribution(){
        this({1, 1, 1}); 
    }  

    public ActionDistribution(double[] weights){  
        this.mWeights = weights;
    }
}

but this results in the error:

Array initializer is not allowed here

Any ideas as to how I can set up a constructor to handle a default array without the use of static factory methods or builder patterns? Bonus points if you have a solution that works for any generic type (and not just arrays), and even more points for explaining why this is possible for simpler data types and not arrays.

Edit: In case it is not clear, in a working form of the code above, I am hoping to later call

MyClass myClass = new MyClass();

and have it result in a myClass object with a field myClass.mWeights of value {1, 1, 1}

topher217
  • 1,188
  • 12
  • 35

3 Answers3

3

You most likely want to use new double[] { ... } syntax:

class MyClass{

    public MyClass(){
        this(new double[] { 1, 1, 1});
    }

    public MyClass(double[] weights){
        this.mWeights = weights;
    }
}

The array creation syntax is explained in §JLS 15.10.1. Array Creation Expressions. As per §10.6. Array Initializers:

An array initializer may be specified in a field declaration (§8.3, §9.3) or local variable declaration (§14.4), or as part of an array creation expression (§15.10.1), to create an array and provide some initial values.

In your case you are not assigning a field or a local variable so you can't use array initializer shorthand. Method (and constructor) parameters require full array creation syntax.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
  • Yes! Thank you. Care to comment as to why you can pass simpler types in without this syntax? – topher217 Nov 20 '19 at 10:12
  • I see, so maybe there is no generalized answer to how to setup a default value for use in a constructor of any given data type as it appears to depend on the data type. In summary, you need to ensure the object of type T is initialized within the empty constructor according to its data type initialization rules, and this proper initialization syntax needs to be completely contained within the `this()` call. Makes sense, but maybe [this](https://stackoverflow.com/a/58951809/2760299) makes more sense from a readability standpoint? Especially if you are going to have more than one parameter. – topher217 Nov 20 '19 at 10:36
2

You can use .. syntax as well:

public class MyClass {

    double[] mWeights;

    public MyClass(double... mWeights) {
        mWeights = mWeights;
    }
    public MyClass(){
        this(1, 2, 3);
    }

}
Boris Zhguchev
  • 313
  • 4
  • 12
  • This also seems to work, but I am not familiar with this syntax. Can you point me some documentation to read up on this? I would like to compare the pros/cons of using this over [this](https://stackoverflow.com/a/58951656/2760299) answer (Finding the ... string on google is not so easy it seems :P) – topher217 Nov 20 '19 at 10:15
  • 1
    it is syntax sugar for arrays ... As a matter of fact, it is the same construction as [this](https://stackoverflow.com/a/58951656/2760299) ... You can have a quick look to [varargs](https://docs.oracle.com/javase/8/docs/technotes/guides/language/varargs.html) – Boris Zhguchev Nov 20 '19 at 10:19
  • Thanks for the links. I read through the varargs link and noticed: >Varargs can be used only in the final argument position So how would you handle the case of multiple array type parameters with this syntax? i.e. `public MyClass(double... mWeights1, double... mWeights2)` will throw an error related to the quoted varargs docs. `public MyClass(double... mWeights1, mWeights2)` not `public MyClass(double... {mWeights1, mWeights2})` appear to work either. Any links to a doc generalizing the syntax of the ellipses? – topher217 Nov 20 '19 at 10:44
  • in case of 'MyClass(double... mWeights1, double... mWeights2)' it will not work and throw a compile exception. The opposite question - What case is it needed? – Boris Zhguchev Nov 20 '19 at 12:02
  • I was asking if you knew of any place that details the general syntax of how to use the varargs ellipses as I didn't find the Oracle doc very detailed. Although my example code is written for a single parameter, my actual use case involves multiple parameters, so I was hoping to understand the syntax of this a bit better, such that I could try it out with multiple parameters. From the examples [here](https://www.programiz.com/java-programming/varargs) it looks like it is useful for cases where you hope to loop through a single variable length array. – topher217 Nov 20 '19 at 12:42
  • A few drawbacks seem to be that it doesn't handle multiple arrays, or multiple types. So for the specific code I have above as an example, it works fine, but for general use, I think the other answers may be more applicable. – topher217 Nov 20 '19 at 12:43
1

You could just use your second approach an leave your constructor empty instead:

public class MyClass {

    double[] mWeights = new double[] {1, 1, 1};  

    public ActionDistribution(){}  

    public ActionDistribution(double[] weights){  
        this.mWeights = weights;
    }
}

Ititializing your class with the default empty constructor will leave your weights as you initialized them in your Class field with {1, 1, 1}.

T A
  • 1,677
  • 4
  • 21
  • 29
  • 1
    I like this. Seems cleaner than the other solutions. – topher217 Nov 20 '19 at 10:21
  • 1
    It also creates a redundant `mWeights` array that will be discarded by `double[]` constructor. Perhaps JVM is smart enough to optimize this use case, but it feels sloppy. – Karol Dowbecki Nov 20 '19 at 10:24
  • @KarolDowbecki As you said it will be discarded, so there won't be any memory issue. I doubt this will have any performance impact. – T A Nov 20 '19 at 10:35
  • @KarolDowbecki, at least for my use case, I'm not too worried about this, even if there were some memory drawbacks, as this answer would make the code much more readable for multiple parameters. Though both answers potentially have their own pros/cons. – topher217 Nov 20 '19 at 10:39