Private/protected no-arg constructor with @MapConstructor/@Immutable?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Private/protected no-arg constructor with @MapConstructor/@Immutable?

Damir Murat
I can't find an option with @MapConstructor/@Immutable to modify the visibility 
of the generated public no-arg constructor. Is it possible to add it if it makes sense?

The idea is to prevent application code from using it unintentionally, while still 
allowing frameworks to access it via reflection.

Tnx
Reply | Threaded
Open this post in threaded view
|

Re: Private/protected no-arg constructor with @MapConstructor/@Immutable?

paulk_asert
Administrator
You want @VisibilityOptions

import groovy.transform.*
import static groovy.transform.options.Visibility.PRIVATE

@Immutable
@VisibilityOptions(PRIVATE)
class Foo {
  String a, b
  static make(Map params) {
    new Foo(params)
  }
}

assert Foo.make(a: 1, b: 2).toString() == 'Foo(1, 2)'
// ACC_PRIVATE = 0x0002 (*3 for tuple, map, no-arg)
assert Foo.declaredConstructors*.modifiers == [2, 2, 2]
// ACC_PUBLIC = 0x0001, ACC_STATIC = 0x0008
assert Foo.getDeclaredMethod('make', Map).modifiers == 9


And below:
image.png
 

Virus-free. www.avast.com

On Tue, Oct 6, 2020 at 6:32 PM Damir Murat <[hidden email]> wrote:
I can't find an option with @MapConstructor/@Immutable to modify the visibility 
of the generated public no-arg constructor. Is it possible to add it if it makes sense?

The idea is to prevent application code from using it unintentionally, while still 
allowing frameworks to access it via reflection.

Tnx
Reply | Threaded
Open this post in threaded view
|

Re: Private/protected no-arg constructor with @MapConstructor/@Immutable?

Damir Murat
Yes, I could benefit from static factory methods. Thanks for giving me an idea. 
But I was thinking only about restricting the visibility of the default no-arg constructor. 

As both @MapConstructor and @TupleConstructor have a visibilityId parameter used 
for fine-grained tuning of visibility, I expected a similar option for the default no-arg 
constructor too. Maybe something like the noArgVisibilityId parameter in @MapConstructor.

Tnx,
Damir
Reply | Threaded
Open this post in threaded view
|

Re: Private/protected no-arg constructor with @MapConstructor/@Immutable?

paulk_asert
Administrator
Yes, you can get rid of the no-arg constructor and control visibility for map and tuple constructors
but can't currently control visibility for the no-arg constructor. We'd need an annotation attribute
something like what you suggested. It's a little strange in that the visibilityId would control visibility
for both the map and no-arg variations unless the noArgVisibilityId attribute was set. Also, if you
set noArg=false for @MapConstructor and defaults=true for @TupleConstructor, you get the
no-arg constructor from the tuple constructor defaults with no way to set the visibility independently
for the 2 or more related constructors.

import groovy.transform.*
import static groovy.transform.options.Visibility.PRIVATE

@Immutable
@MapConstructor(noArg=false)
@TupleConstructor(defaults=true, visibilityId='hidden')
@VisibilityOptions(id = 'hidden', value = PRIVATE)
class Foo {
    String a, b, c
}

assert Foo.declaredConstructors*.modifiers == [2, 2, 2, 2, 1]




Cheers, Paul.

Virus-free. www.avast.com

On Tue, Oct 6, 2020 at 10:49 PM Damir Murat <[hidden email]> wrote:
Yes, I could benefit from static factory methods. Thanks for giving me an idea. 
But I was thinking only about restricting the visibility of the default no-arg constructor. 

As both @MapConstructor and @TupleConstructor have a visibilityId parameter used 
for fine-grained tuning of visibility, I expected a similar option for the default no-arg 
constructor too. Maybe something like the noArgVisibilityId parameter in @MapConstructor.

Tnx,
Damir

Virus-free. www.avast.com
Reply | Threaded
Open this post in threaded view
|

Re: Private/protected no-arg constructor with @MapConstructor/@Immutable?

Damir Murat
> It's a little strange in that the visibilityId would control visibility for both the map and no-arg variations 
> unless the noArgVisibilityId attribute was set.

Yes, it is strange a little, but not unbearable :-)

> Also, if you set noArg=false for @MapConstructor and defaults=true for @TupleConstructor, you get the
> no-arg constructor from the tuple constructor defaults with no way to set the visibility independently
> for the 2 or more related constructors.

Actually, this might work for me, since I want only a map constructor anyway. But, I guess it's not 
appropriate as a general solution.

Is it possible to write an AST transform that will reduce the visibility of the default constructor if it exists? 
I mean, MapConstructorASTTransformation and TupleConstructorASTTransformation are assigned to the
CANONICALIZATION phase, and that new transformation should execute after them, I believe. Is it possible 
to arrange such ordering without changing Groovy code?

Tnx,
Damir

Reply | Threaded
Open this post in threaded view
|

Fwd: Private/protected no-arg constructor with @MapConstructor/@Immutable?

paulk_asert
Administrator

Accidentally left off the list.

---------- Forwarded message ---------
From: Paul King <[hidden email]>
Date: Wed, Oct 7, 2020 at 7:52 AM
Subject: Re: Private/protected no-arg constructor with @MapConstructor/@Immutable?
To: Damir Murat <[hidden email]>


It is certainly possible to write an additional helper AST transform. It is complicated somewhat for the TupleConstructor case by the fact that it is not until the class generation phase that the non-full tuple related constructors are created. The easiest approach would be to only support changing the no-arg constructor produced from @MapConstructor. There is currently a little wiggle room in the implementation details which while not guaranteed forever is likely not to change until possibly Groovy 5 or beyond. It allows a trick of manually using the normally internal @Generated marker interface as follows:

import groovy.transform.*
import static groovy.transform.options.Visibility.PRIVATE

@Immutable
@MapConstructor(noArg=false)
@TupleConstructor(visibilityId='hidden')
@VisibilityOptions(id = 'hidden', value = PRIVATE)
class Foo {
    String a, b, c
    @Generated
    private Foo(){ this([:]) }
}

// private no-arg, public map, private full tuple constructor
assert Foo.declaredConstructors*.modifiers == [2, 1, 2]


Cheers, Paul.


On Wed, Oct 7, 2020 at 4:56 AM Damir Murat <[hidden email]> wrote:
> It's a little strange in that the visibilityId would control visibility for both the map and no-arg variations 
> unless the noArgVisibilityId attribute was set.

Yes, it is strange a little, but not unbearable :-)

> Also, if you set noArg=false for @MapConstructor and defaults=true for @TupleConstructor, you get the
> no-arg constructor from the tuple constructor defaults with no way to set the visibility independently
> for the 2 or more related constructors.

Actually, this might work for me, since I want only a map constructor anyway. But, I guess it's not 
appropriate as a general solution.

Is it possible to write an AST transform that will reduce the visibility of the default constructor if it exists? 
I mean, MapConstructorASTTransformation and TupleConstructorASTTransformation are assigned to the
CANONICALIZATION phase, and that new transformation should execute after them, I believe. Is it possible 
to arrange such ordering without changing Groovy code?

Tnx,
Damir

Reply | Threaded
Open this post in threaded view
|

Re: Fwd: Private/protected no-arg constructor with @MapConstructor/@Immutable?

Damir Murat
Thank you for your help. You are most kind, sir :-)

I ended up creating a helper AST transform. If you are interested, you can see it here:
https://github.com/croz-ltd/klokwrk-project/commit/6f297c96db900d8dabffb45f2a5a38c60e442418

To complete my @Immutable customization story, I also created an AST transform that 
adds my common post-check in the generated map constructor. You can find it here: 
https://github.com/croz-ltd/klokwrk-project/commit/724126dac172fecba74a913199ead87223eb4c69

Of course, any comments or suggestions are more than welcome :-)

Thank you,
Damir