Search code examples
groovyconstructorinstantiationdefault-constructor

How to prevent default constructor from being used?


I grew up with Java compiler auto-generating a default constructor whenever there's no explicit constructor in the class; and not generating when I have any explicit constructors.

As far I understand a constructor defines the required dependencies and the properties define the optional dependencies (most likely with default values... set by the constructors). Being able to call <init>() when it is not defined is just plain wrong in object oriented code, if you adhere to the above rule (which I picked up empirically during my career).

Here's a simple test I tried and noticed that even with explicit constructor it's easy to instantiate the object without args. How can I make this program fail at compile time or runtime time at the lines marked with ????

class TestGroovy {
    private final String name
    TestGroovy(String name) {
        this.name = name
    }

    static void main(String[] args) {
        testStatic()
        println()
        testDynamic()
        println()
        testReflection()
    }

    @groovy.transform.CompileStatic
    static void testStatic() {
        println new TestGroovy("static");
        println "compile error"
        // Groovyc: [Static type checking] - Cannot find matching method TestGroovy#<init>().
        // Please check if the declared type is right and if the method exists.
        //println new TestGroovy(); // correct
    }

    static void testDynamic() {
        println new TestGroovy("dynamic");
        println new TestGroovy(); // ???
    }

    static void testReflection() {
        println TestGroovy.class.newInstance([ "reflection" ] as Object[]);
        println TestGroovy.class.newInstance(); // ???
    }

    @Override String toString() { return "Name: ${name}"; }
}

Output:

Name: static
compile error

Name: dynamic
Name: null

Name: reflection
Name: null

Expected: RuntimeException instead of Name: null.

I tried to find the corresponding section in the Groovy documentation, but I didn't find anything really relevant. The keywords I looked for are default constructor, no-arg constructor, no-args constructor, no-arguments constructor.

Though here's a remotely related one:

Named argument constructor
If no constructor is declared, it is possible to create objects [...]

As I understand the positional constructors are the ones that are declared and Java-like and you can use named constructors if there are no explicit positional ones. I had an idea that the default constructor call above (in testDynamic()) is actually working because it's calling the named constructor with an empty map, but I ruled this out real quick as the named constructor section starts with "If no constructor is declared", and I clearly have one.


Solution

  • In Groovy you can call a single parameter method without arguments. Null will be used in place then. (Unless the parameter has a primitive type, then the call fails). So it is perfectly legal and defined for Groovy to do this for constructors as well. It is planed to remove that feature in the future. Because of that we decided that the static groovy compiler will not ever support it. This is why the static compiler fails compilation here. So it is not the case, that a no-args constructor is generated, the existing String compatible values taking constructor is called with a null value. If you absolutely wanted to prevent this, you could try meta programming to replace the constructor and add a null check. Groovy won't throw an Exception here for you