You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@groovy.apache.org by Damir Murat <da...@gmail.com> on 2020/10/06 08:31:54 UTC

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

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

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

Posted by Damir Murat <da...@gmail.com>.
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

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

Posted by Paul King <pa...@asert.com.au>.
Accidentally left off the list.

---------- Forwarded message ---------
From: Paul King <pa...@asert.com.au>
Date: Wed, Oct 7, 2020 at 7:52 AM
Subject: Re: Private/protected no-arg constructor with
@MapConstructor/@Immutable?
To: Damir Murat <da...@gmail.com>


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 <da...@gmail.com> 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
>
>

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

Posted by Damir Murat <da...@gmail.com>.
> 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


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

Posted by Paul King <pa...@asert.com.au>.
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.

<https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail>
Virus-free.
www.avast.com
<https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail>
<#m_-1155727755934344353_DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>

On Tue, Oct 6, 2020 at 10:49 PM Damir Murat <da...@gmail.com> 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
>

<https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail>
Virus-free.
www.avast.com
<https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail>
<#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>

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

Posted by Damir Murat <da...@gmail.com>.
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

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

Posted by Paul King <pa...@asert.com.au>.
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


See also:
https://groovy-lang.org/metaprogramming.html#xform-VisibilityOptions
And below:
[image: image.png]


<https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail>
Virus-free.
www.avast.com
<https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail>
<#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>

On Tue, Oct 6, 2020 at 6:32 PM Damir Murat <da...@gmail.com> 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
>