Support for multiple constructors

1,419 views
Skip to first unread message

Tuukka Mustonen

unread,
Mar 17, 2010, 8:06:30 AM3/17/10
to Project Lombok
Hi everyone,

What might be the status for supporting multiple constructors? I am
using JPA, which requires a non-variable constructor but I also want
to have @NonNull generated constructor. Manually adding the non-args
constructor wipes off the Lombok generated constructor, which forces
me to manually type that as well :/

There was an earlier conversation:

http://groups.google.com/group/project-lombok/browse_thread/thread/e648ff4e99d9d7d0/e25fe4d2bf092128?lnk=gst&q=constructors#e25fe4d2bf092128

...but I couldn't find the reply to all button, so I'm starting a new
thread.

Regards,
Tuukka

PS. Thanks for bringin in this project! Would you see any benefits in
using this in Groovy/Scala environments (both offer a brief syntax to
defining JavaBeans) or is this for plain Java only?

Reinier Zwitserloot

unread,
Mar 17, 2010, 9:23:17 PM3/17/10
to Project Lombok
Both myself and roel do almost all of our development in java. If
someone wants to extend the concept, we're all ears, but I doubt we're
going to be doing most of the work on that.

We've visited and revisited and rerererererevisited constructor
generation for @Data and we're still not entirely sure how to do it.
We're working on the Flying Turtle release right now which is a lot of
background work on making the base more robust, so we're not currently
looking too much into updating the transformers themselves. We'll get
back to that and tackle this issue when we've delivered this update :)

On Mar 17, 1:06 pm, Tuukka Mustonen <tuukka.musto...@gmail.com> wrote:
> Hi everyone,
>
> What might be the status for supporting multiple constructors? I am
> using JPA, which requires a non-variable constructor but I also want
> to have @NonNull generated constructor. Manually adding the non-args
> constructor wipes off the Lombok generated constructor, which forces
> me to manually type that as well :/
>
> There was an earlier conversation:
>

> http://groups.google.com/group/project-lombok/browse_thread/thread/e6...

Tuukka Mustonen

unread,
Mar 18, 2010, 3:54:10 AM3/18/10
to project...@googlegroups.com
Actually, I really like to hear that you pay consideration to what features should be included and what not. Feature-bloated products give me creeps... I personally have feeling that Lombok would be quite useless in Groovy/Scala worlds, but maybe a better knowing person can confirm/correct me on this as I've only glanced at those.

Regarding support for multiple constructors, why wouldn't you either:

1) Support creation of no-args constructor in addition to @NonNull based creation. As JPA requires this (maybe Hibernate, JDO and others as well?) I suppose this is a very common case. This could be achieved easily(?) by

@Data(noArgsConstructor = true) or @Data(constructor = SomeEnum.NO_ARGS)

or

@Constructor(args = false)

or something like that? I think the point here is that creating a no-args constructor doesn't require anything special. Just throw it in there :)

2) Allow people to manually define additional constructors without them wiping of the Lombok generated one(s). Is there a technical reason for not allowing this currently?

Regards,
Tuukka



--
You received this message because you are subscribed to the Google
Groups group for http://projectlombok.org/

To post to this group, send email to project...@googlegroups.com
To unsubscribe from this group, send email to
project-lombo...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/project-lombok?hl=en

Reinier Zwitserloot

unread,
Mar 18, 2010, 4:01:25 AM3/18/10
to project-lombok
If no-args constructor was all there was, that's the logical solution, though probably we'd go with a special @Constructor annotation (We're trying to keep @Data lean; it controls so many things that if we add settings for all of them, even perfectly reasonable things like noargs constructors, it'll soon have 50 options, that wouldn't be a good thing). However, the current  constructor generation isn't really something we're proud of either. It only includes variables that are either final or non-null, skipping variables that are fully mutable. That seems arbitrary and doesn't feel right. So, we'd like for it to be simple to specify exactly what you do and do not want in your constructor. Couple this with the noargs issues and you're looking at wanting to create multiple constructors, each with their own list of fields that should be included.

Then too we want builders for fluent APIs, to add yet another dimension here.

So, what do we do? Create a @Constructors annotation where you can provide a list of strings, each string containing a comma-separated list of field names, so that a constructor is generated for each such comma-separated string? Where do builders come into this story? It also feels rather ugly and inconvenient to have to list out, in strings, field names. Refactoring a field name wouldn't cause this string to get updated.

We could also flip it around and let you annotate the fields you want in a constructor, tagging it with an array of strings that are arbitrary identifiers. E.g something like:

@Data
public class Test {
    @InConstructor("foo", "bar")
    private String a;
    @InConstructor("bar")
    private String b;
}

would create two constructors, one with just 'a', the other with both 'a' and 'b'. Other than matching that 'a' and 'b' should show up in the same constructor together, the names "foo" and "bar" have no effect.

Fun idea but not entirely convinced its the best we can do.

--Reinier Zwitserloot

Moandji Ezana

unread,
Mar 18, 2010, 4:19:07 AM3/18/10
to project...@googlegroups.com
On Thu, Mar 18, 2010 at 9:01 AM, Reinier Zwitserloot <rein...@gmail.com> wrote:
However, the current  constructor generation isn't really something we're proud of either. It only includes variables that are either final or non-null, skipping variables that are fully mutable. That seems arbitrary and doesn't feel right.

It feels exactly right, to me. Final fields are required and must be given to the constructor, everything else is optional, though it might have a default value.

The no-args constructor for JPA frameworks is a need, but I think it should be addressed in a JPA-aware plugin, as it is a need that arises from tools, rather than from the language itself. It would, for example, be sufficient to pick up on the @Entity annotation and add a protected no-arg constructor, if none exists.

All the proposals so far just seem ugly, which should be reason enough to keep it out. A JPA plugin could also serve as testing ground for this kind of feature, before deciding if it's worth putting into the core and if so, how.

Moandji

Tuukka Mustonen

unread,
Mar 18, 2010, 6:04:57 AM3/18/10
to project...@googlegroups.com
I can understand the pains of syntax/approach for creating multiple constructors. If there's no neat solution, maybe it shouldn't be implemented at all.

It is true, that JPA specific issues would better be separated into an extension/plugin. However, having a noargs constructor _may_ be a very common need (just a guess) and implementation should be easy, so supporting that would in vanilla product would sound logical to me. So, I don't suggest fully-featured constructor definitions (because they are problematic and require very verbose syntax) but I suggest a support for creation of simple noargs constructors. Other JPA/JDA/iBATIS/Hibernate/whatever support should be separated into an extension (reading @Column(nullable = true/false) and such attributes).

Or then, maybe the better approach would be to really move those features into extensions but allow this as suggested in my previous mail:


2) Allow people to manually define additional constructors without them wiping of the Lombok generated one(s). Is there a technical reason for not allowing this currently?

What do you think about this?


On Thu, Mar 18, 2010 at 10:19 AM, Moandji Ezana <mwa...@gmail.com> wrote:
On Thu, Mar 18, 2010 at 9:01 AM, Reinier Zwitserloot <rein...@gmail.com> wrote:
However, the current  constructor generation isn't really something we're proud of either. It only includes variables that are either final or non-null, skipping variables that are fully mutable. That seems arbitrary and doesn't feel right.

It feels exactly right, to me. Final fields are required and must be given to the constructor, everything else is optional, though it might have a default value.

I must totally agree on this. Of course having additional constructors would be nice in order to write:

MyObject o = new MyObject(1, 2, 3);

and not

MyObject obj = new MyObject(1);
obj.setSecond(2);
obj.setThird(3);

What about creating setters in such a way that:

public MyObject setSecond(Integer second) {
    this.second = second;
    return this;
}

This would allow to do:

MyObject obj = new MyObject(1)
    .setSecond(2)
    .setThird(3);

Though not much less verbose, a bit at least. But I suppose that's not legal setter for a JavaBean...

Tuukka



The no-args constructor for JPA frameworks is a need, but I think it should be addressed in a JPA-aware plugin, as it is a need that arises from tools, rather than from the language itself. It would, for example, be sufficient to pick up on the @Entity annotation and add a protected no-arg constructor, if none exists.

All the proposals so far just seem ugly, which should be reason enough to keep it out. A JPA plugin could also serve as testing ground for this kind of feature, before deciding if it's worth putting into the core and if so, how.

Moandji

--

Moandji Ezana

unread,
Mar 18, 2010, 6:28:42 AM3/18/10
to project...@googlegroups.com
On Thu, Mar 18, 2010 at 11:04 AM, Tuukka Mustonen <tuukka....@gmail.com> wrote:
2) Allow people to manually define additional constructors without them wiping of the Lombok generated one(s). Is there a technical reason for not allowing this currently?

What do you think about this?

I guess the constructor would have to be annotated with something like @IgnoredByLombok.

What about creating setters in such a way that:

public MyObject setSecond(Integer second) {
    this.second = second;
    return this;
}

I think this was debated and that it was judged best to stick as closely as possible to the JavaBeans spec. My recollection is very fuzzy, though.

Moandji

mkimberlin

unread,
Mar 18, 2010, 9:43:55 AM3/18/10
to Project Lombok
It seems to me that the default behavior of final and non-null fields
is appropriate in most circumstances. However, there are certainly
situations where another constructors are needed. I don't think that
the idea of leaving no-arg constructors to particular plugins is the
appropriate solution though. That requirement is cross-cutting enough
across various frameworks that it would be being implemented
repeatedly.

What about an annotation like this:

@Constructor - for no args
@Constructor({"this", "that", "theother"}) - for a constructor that
includes the fields this, that and theother

If you want/need another constructor, you add another annotation. I
can't think of a good reason that you would ever need more than two of
these (if that), so it shouldn't cause too much 'annotation pixie-
dust' sprinkled on the class.

-michael

Moandji Ezana

unread,
Mar 18, 2010, 11:34:00 AM3/18/10
to project...@googlegroups.com
On Thu, Mar 18, 2010 at 2:43 PM, mkimberlin <mkimb...@gmail.com> wrote:
@Constructor({"this", "that", "theother"})

Maybe it's just me, but strings-as-field-names is a code smell. Lombok eliminates boilerplate, but custom constructors are just that - custom. And replacing boilerplate by unrefactorable strings seems like a step backwards, especially as constructors are less boilerplate-y than getters/setters.

Currently, if you need a custom getter - one that does caching, for example - you'd write it yourself. Similarly, if you need an arbitrary combination of constructors, especially if you "can't think of a good reason that you would ever need more than two" and Reinier doesn't want to add a specific no-arg constructor flag, is writing both yourself so bad?

Moandji

Reinier Zwitserloot

unread,
Mar 18, 2010, 1:04:32 PM3/18/10
to Project Lombok
No-args as a specific use case is worth considering, _BUT_, the only
way this problem can even occur is if you use @NonNull annotations,
and with the (public) no-args constructor, those @NonNull annotations
aren't actually guaranteed. Isn't it more technically correct to just
remove the annotations? I sort of like the idea of triggering off of
@Entity to also generate a no-args constructor if possible, mostly
because the entire JPA POJO concept is exactly the kind of thing that
causes screwed up class design, such as @NonNull annotations on fields
that can nevertheless end up as null due to a required no-args
constructor.

setters returning 'this' has come up many times before. We're not
going to do it for two reasons:

1) A setter is a well-defined concept in the java ecosystem that does
*NOT* include returning itself. If lombok did generate such setters
that would be very surprising. As you say, it's not legal according to
the javabean spec.

2) More usually, self-returning methods omit the 'set' in their name.

We might be up for something like @Data(fluent=true) with the same
parameter allowed on @Getter and @Setter. This would mean that:

The 'get'/'is' and 'set' aren't included. You get "fieldName()" to get
and "fieldName(Type newValue)" to set, and the setter returns self.

We could also keep lombok's constructor when you write your own but
this has two problems:

1) How do you tell lombok NOT to generate it? If there's no way you'd
have to take out your @Data annotation, add back @EqualsAndHashCode
and @ToString, and @Getter and @Setter on each field, which is rather
a lot of work.

2) Lombok (currently) runs without resolution. It's a really bad idea
to overload a method (or a constructor) with the same # of arguments
where one of the arguments is a subclass of the other and the rest are
equal. However, lombok cannot detect that this is happening, so the
rule would have to be that lombok ONLY skips generating its own
constructor when you've got your own constructor with a parameter list
that matches every type down to the last character. Which would be
inconsistent with the rules for methods (where I strongly feel lombok
should not generate a method if you have one with the same name. We
might revisit that when we have resolution so we can check if there's
no chance of confusion between the two overloaded methods).

The current constructor situation is not optimal, but we'd like to
replace it with something that feels right. None of these solutions so
far feel completely correct. If at some point we must conclude there
is no right answer, then we'll go with these suggestions, but we still
have hope there's an answer out there.

On Mar 18, 11:04 am, Tuukka Mustonen <tuukka.musto...@gmail.com>
wrote:


> I can understand the pains of syntax/approach for creating multiple
> constructors. If there's no neat solution, maybe it shouldn't be implemented
> at all.
>
> It is true, that JPA specific issues would better be separated into an
> extension/plugin. However, having a noargs constructor _may_ be a very
> common need (just a guess) and implementation should be easy, so supporting
> that would in vanilla product would sound logical to me. So, I don't suggest
> fully-featured constructor definitions (because they are problematic and
> require very verbose syntax) but I suggest a support for creation of simple
> noargs constructors. Other JPA/JDA/iBATIS/Hibernate/whatever support should
> be separated into an extension (reading @Column(nullable = true/false) and
> such attributes).
>
> Or then, maybe the better approach would be to really move those features
> into extensions but allow this as suggested in my previous mail:
>

> *2) Allow people to manually define additional constructors without them


> wiping of the Lombok generated one(s). Is there a technical reason for not

> allowing this currently?*


>
> What do you think about this?
>
> On Thu, Mar 18, 2010 at 10:19 AM, Moandji Ezana <mwa...@gmail.com> wrote:

> > Groups group forhttp://projectlombok.org/


>
> > To post to this group, send email to project...@googlegroups.com
> > To unsubscribe from this group, send email to

> > project-lombo...@googlegroups.com<project-lombok%2Bunsubscribe@go oglegroups.com>

Reinier Zwitserloot

unread,
Mar 18, 2010, 1:08:13 PM3/18/10
to Project Lombok
This suggestion has two problems:

1. You may not annotate one node with the same annotation more than
once. Therefore, either this @Constructor annotation will only let you
create 1 constructor, which sort of defeats the point, or, we do
something a bit nuts. For example:

@Constructor("this, that, theother") //Generate one constructor with
listed fields
@Constructor({"this, that, the other", "") //Generate that one AND a
noargs one.

or:

@Constructors({
@Constructor("this", "that", "theother"),
@Constructor
})

and possibly allowing just 1 @Constructor as well if one is all you
need, but this makes it non-obvious what someone should do when they
want another constructor. The jump to "wrap @Constructor in
@Constructors and THEN add the second one" is not exactly something
that's going to casually pop into your mind.

Last but certainly not least, including field names is a code smell.
We're already unhappy with our choice to go with enumerating field
names in strings for the exclude/of parameters of EqualsAndHashcode
and ToString.

mkimberlin

unread,
Mar 18, 2010, 3:59:26 PM3/18/10
to Project Lombok
Yeah, I clearly didn't think very long on the multiple annotations
thing. Not sure where my head was...

I agree that field names as strings are generally a code smell.
However, it's also the most common way to reference fields in
annotations that are not field level or to reference fields from other
objects in annotations. You simply can't reference them directly and
any other approach would require making some sort of constant mapping
similar to the @InConstructor approach Reinier mentioned which would
be confusing, I think. It's definitely more resistant to common
refactorings, but it gives the impression of naming constructors
arbitrarily. I'm inclined to think that a numbering would be less
confusing and error prone than names if that approach were used.

Moandji: I should note that I'm not the one who asked for this
feature. I was simply taking part in the conversation. I don't think
writing both is "so bad", personally. I have no problem with leaving
this feature out altogether and having things remain as they are. I
was simply introducing a thought to the conversation.

That said, the more I consider this the more I think that a single
customizable constructor plus a no-arg constructor should be plenty
for almost all situations, no?
So if this feature was deemed necessary, you could always use a
combination of @InConstructor and @Constructor but limit it to a
single @Constructor annotation.

So for a custom constructor:

public class Bogus {
@InConstructor private string thisOne;
@InConstructor private string thatOne;
@InConstructor private string theOtherOne;
}

or if you need a no-arg as well:

@Constructor(noarg=true)
public class Bogus {
@InConstructor private string thisOne;
@InConstructor private string thatOne;
@InConstructor private string theOtherOne;
}

or just a noarg:

@Constructor(noarg=true)
public class Bogus {
private string thisOne;
private string thatOne;
private string theOtherOne;
}

or non-null, custom and noarg constructors:

@Constructor(nonnull=true, noarg=true)
public class Bogus {
@InConstructor private string thisOne;
@InConstructor private string thatOne;
@InConstructor private string theOtherOne;

@NonNull private string imNotNull;

}

Something to that effect...

Reinier Zwitserloot

unread,
Mar 18, 2010, 4:59:04 PM3/18/10
to project-lombok
Keep the ideas coming. I'm personally partial to only allowing the following mix:

1. A no-args constructor, if you want it. Obviously off the table if any of the fields are final.

2. A some args constructor. The choice here is whether you want *ALL* parameters or only parameters that are needed, either because of final or because of @NonNull.

So, that's at most 2 constructors. The information lombok would need to figure it out involve:

1. No-args or not
2. all parameters or only required parameters.

Because @Data is supposed to be lean, we'll probably create a new @Constructor or @Constructors or @FieldConstructors annotation that you can use by itself separate from @Data, and which will be the carrier of these flags, which will probably end up being 2 booleans, named 'noargs' and 'allFields' or some such. I'd also like for these constructors to forward the field to the setter, which doesn't currently happen. We may as an optimization not do this if we generate the setter (though the JVM is pretty good at inlining this stuff so not sure if thats worth doing), but if you write your own, we'll use it.

I believe it should handle all pragmatic use-cases, no? Then the only significant smellage is that as a programmer you sort of have to guess that by default lombok generates 1 constructor that includes all non-final or @NonNull marked fields, unless you use @Constructors to change this.



--Reinier Zwitserloot



--
You received this message because you are subscribed to the Google
Groups group for http://projectlombok.org/

To post to this group, send email to project...@googlegroups.com
To unsubscribe from this group, send email to
project-lombo...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/project-lombok?hl=en

To unsubscribe from this group, send email to project-lombok+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.

mkimberlin

unread,
Mar 18, 2010, 9:01:26 PM3/18/10
to Project Lombok

+1

I like this. I think the whole mix How could we ever know for sure if
Sylvia Browne were really psychic. A couple of college students in NY
just checked! tinyurl.com/yz7fq8o match of fields idea would just be
more clutter than it's worth for very little benefit. All, required,
none...anything else is probably just a sign that something you are
doing is stinky.

Good call.

Also, agreed that it doesn't belong in @Data. @Constructors is
probably the most natural and obvious.

Should a feature request issue be created for this? If you think it's
worth implementing, I'll write one up.

> > Groups group forhttp://projectlombok.org/


>
> > To post to this group, send email to project...@googlegroups.com
> > To unsubscribe from this group, send email to

> > project-lombo...@googlegroups.com<project-lombok%2Bunsubscribe@go oglegroups.com>

Reinier Zwitserloot

unread,
Mar 18, 2010, 9:53:47 PM3/18/10
to project-lombok
Yes, please write one up so we don't forget. I talked with Roel about it and he came up with something very similar as well, just didn't have time to post it yet. There's a minor change we thought up. Do you like it? Here it is:

Instead of the 'noargs=false' and 'allFields=true' aspect, instead, there's an enum, and the @Constructors annotation takes a list of them. The values of the enum are:

public enum ConstructorType {
    NOARGS, MINIMAL, ALL
}

public @interface Constructors {
    ConstructorType[] value() default {ConstructorType.MINIMAL};
}

This has the additional benefit that, if you really need it, you can use this to generate any combination. So, if you want a noargs constructor, a minimal (all @NonNull fields) constructor, *AND* a constructor with all fields, you could:

@Constructors(ConstructorType.NOARGS, ConstructorType.MINIMAL, ConstructorType.ALL)
public class Foo { ... }

and you can list as many or as few as you like. If you don't list any, the default is 'MINIMAL', which is its current behaviour. We'll most likely end up with different names.

The same trick can be applied for fluent getters/setters:

public enum GetSetStyle {
    FLUENT, BEAN, NONE
}

public @interface Data {
    GetSetStyle style() default GetSetStyle.BEAN;
}

(fluent getters/setters means each getter and setter is named like the field and not with 'get' or 'set' in front of it, and setters return this instead of void).

--Reinier Zwitserloot



Groups group for http://projectlombok.org/


To post to this group, send email to project...@googlegroups.com
To unsubscribe from this group, send email to
project-lombo...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/project-lombok?hl=en

To unsubscribe from this group, send email to project-lombok+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.

Moandji Ezana

unread,
Mar 19, 2010, 4:31:55 AM3/19/10
to project...@googlegroups.com
I like the enums. However, going back to the original post, the problem was to generate a no-args constructor for JPA. If you're also going to have a custom constructor, you probably want the no-args one to be protected, rather than public. Would that be possible?

Moandji

Reinier Zwitserloot

unread,
Mar 19, 2010, 9:01:56 AM3/19/10
to Project Lombok
The enum scheme really can't handle more parameters. Ugh. Back to the
booleans (which would then be replaced by AccessLevels)? We can
actually tell the difference between explicitly stated parameters and
defaults. Therefore by default 'minimal' can be on PUBLIC and the
other two on 'none', but if you specify some other constructor then
minimal defaults to NONE too unless you are explicit about it. Then
you get:

@Constructors - 1 public minimal constructor (implicit if you use
@Data)

@Constructors(noargs=AccessLevel.PROTECTED) - 1 protected noargs and
nothing else.

@Constructors(noargs=AccessLevel.PROTECTED,
minimal=AccessLevel.PUBLIC) - 1 protected noargs, 1 public standard
(minimal).

Feels a bit wordy.

How about:

@Constructors({ConstructorTypes.PROTECTED_NOARGS,
ConstructorTypes.MINIMAL})

Oi vey, that's as long as the AccessLevel based one and actually feels
uglier (and is less flexible unless we add every combination of
constructor type and access level to the enum).

So, this:

@Constructors(noargs=AccessLevel.PROTECTED,
minimal=AccessLevel.PUBLIC)

is the winner then? I'd love some more feedback before we go for it.

mkimberlin

unread,
Mar 19, 2010, 11:53:16 PM3/19/10
to Project Lombok
If the verbosity bothers anyone, there's always the static import
option...then it's only:

@Constructors(noargs=PROTECTED, minimal=PUBLIC)

I don't think this is too bad. Since this is essentially the same as
the booleans way with the little extra feature of access level control
for (almost) free. I think this is reasonable.

mkimberlin

unread,
Mar 19, 2010, 11:54:45 PM3/19/10
to Project Lombok
Hey...I just noticed that I accidentally posted that Sylvia Brown
tweet in that earlier email. oops...not sure how that happened. :)

mkimberlin

unread,
Mar 20, 2010, 12:21:23 AM3/20/10
to Project Lombok
I submitted an issue for this, although it wouldn't let me specify the
type. So one of you will have to change it to Enhancement, I
suppose. I tried to capture the gist of what was said here in the
issue...but, I also provided a link back to this thread, just in case
it's a while before you get around to it and it grows cold in your
mind.

I would offer to give a hand in implementation of it, but I'm afraid
that I have a few too many other pet projects in the air at the
moment. :) Perhaps I'll double back and throw some code toward Lombok
in the future. It's a great project.

Also, Reinier...I would like to pick your brain slightly about the
upcoming release. If there are any significant changes to the
features/functionality of Project Lombok, I would like to update the
article that I wrote on it to include them. Life hasn't given me much
opportunity to keep a close eye on the project, so I'm not sure what
I've missed in the last few months.

-michael

On Mar 19, 8:01 am, Reinier Zwitserloot <reini...@gmail.com> wrote:

Tuukka Mustonen

unread,
Mar 21, 2010, 9:17:57 AM3/21/10
to project...@googlegroups.com
Thank you guys for taking initiative and so eager attitude towards this :)

I don't think having @Constructor for single constructor and switching to @Constructors({@Consutrctor, @Constructor}) would be a bad thing. It is somewhat verbose but it's the common solution to having multiple similar annotations so shouldn't it be well known? If not, users can be made aware of it with one or two lines of documentation (and I think it still feels quite logical). Also, as stated previously, referring to fields via strings and reflection is ugly, but yet a very common approach (which, of course, is not to say that it's a good one). However, losing refactoring support is what I, too, shun. So, overall, the current suggestion of supporting max. 3 constructors with different access levels is flexible enough for the majority of use cases...

However, I would still emphasize the importance of allowing custom constructors in addition to the ones created by Lombok. I'm clearly no match to you in technical domain, so I didn't quite get grasp of what Reinier meant by Lombok being "run without resolution" and overriding the constructors with nearly exact match of arguments. So I'm just stating this: the more features Lombok provides, the sadder it is to give those up. While it might not be common case, cannot there be situations where one does need a fourth constructor? The outcome of the currently suggested implementation would be having to manually write all the 4, not just the last fourth. Similar case is when one wants to customize one of the automatically provided constrcutors, for example one that's provided with MINIMAL enum - customization means manually rewriting that constructor (which is ok) but then one would have to manually type in the NOARGS (not too bad) and ALL (very frustrating) constructors as well. What was wrong with the @IgnoredByLombok annotation suggested by Moandji?

Please ignore the above paragraph if the answers and technical reasoning was already provided by Reinier in an ealier post.

Tuukka



Reinier Zwitserloot

unread,
Mar 21, 2010, 2:53:35 PM3/21/10
to Project Lombok
What 'resolution' means is figuring out that an appearance of, lets
say, "String" somewhere in your java code is in fact referring to
"java.lang.String". We do our own half-baked resolution on types so
that we can figure out that @Getter did in fact refer to
@lombok.Getter and not some other sort of getter but it's not 100%
perfect (especially when star imports are involved, lombok can come to
different conclusions that the official java spec). Also, and perhaps
more importantly, we don't have access to the stucture of *other*
source files. Thus, even we do know something is referring to
"java.lang.String", we don't know what methods are in
"java.lang.String". And, more crucially for this particular case, we
don't know that for example "java.lang.Integer" is a subclass of
"java.lang.Number". Therefore, if we wanted to generate:

public MyClass(Integer x) {}

but you already manually typed:

public MyClass(Number x) {}

generating the integer-based one is probably a bad idea, as having
both constructors around is going to be very confusing. However, we
simply don't know that Integer is a subclass of Number (well, for
java.lang. stuff its obvious, but what if it were your own classes and
not those in java.*?)

What we could do is say that we will forego generating our own stuff
only if you have a method with the same name *AND* the same # of
parameters. That would probably solve most cases, but it wouldn't be
all that natural (you wouldn't know unless you read about it). Then
again, neither is the current situation (where lombok does not
generate something if you have a method with the same name regardless
of parameters).

On Mar 21, 2:17 pm, Tuukka Mustonen <tuukka.musto...@gmail.com> wrote:
> Thank you guys for taking initiative and so eager attitude towards this :)
>
> I don't think having @Constructor for single constructor and switching to
> @Constructors({@Consutrctor, @Constructor}) would be a bad thing. It is
> somewhat verbose but it's the common solution to having multiple similar
> annotations so shouldn't it be well known? If not, users can be made aware
> of it with one or two lines of documentation (and I think it still feels
> quite logical). Also, as stated previously, referring to fields via strings
> and reflection is ugly, but yet a very common approach (which, of course, is
> not to say that it's a good one). However, losing refactoring support is
> what I, too, shun. So, overall, the current suggestion of supporting max. 3
> constructors with different access levels is flexible enough for the
> majority of use cases...
>
> However, I would still emphasize the importance of allowing custom

> constructors *in addition* to the ones created by Lombok. I'm clearly no

> > Groups group forhttp://projectlombok.org/


>
> > To post to this group, send email to project...@googlegroups.com
> > To unsubscribe from this group, send email to

> > project-lombo...@googlegroups.com<project-lombok%2Bunsubscribe@go oglegroups.com>

Tuukka Mustonen

unread,
Mar 23, 2010, 5:29:47 PM3/23/10
to project...@googlegroups.com
Thanks for the awesome and clarifying reply. Helped me to get grasp of things... I still want to rip this wound open, though :)

It sounds like Lombok cannot, in worst case, do anything critical? The worst case (actual error) would be to create duplicate constructor (with identical # of arguments and identical "appearances"). This is something that Lombok can find out and avoid, right?

About the quite-similar-but-not-similar signatures. What's wrong with:

public MyClass(Integer x) {}

and

public MyClass(Number x) {}

Both constructors are sane, though having 2 so similar signatures might be confusing, but user should be aware of both of them, anyway. Having such a case would be quite rare, also? I fail to see any actual problems in this?

Why not just generate the constructors as indicated via annotations (previous discussion) and retain generation on those that have exactly same signature already manually defined?

Humble regards,
Tuukka



Groups group for http://projectlombok.org/


To post to this group, send email to project...@googlegroups.com
To unsubscribe from this group, send email to
project-lombo...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/project-lombok?hl=en

To unsubscribe from this group, send email to project-lombok+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.

Reinier Zwitserloot

unread,
Mar 24, 2010, 7:08:40 PM3/24/10
to project-lombok
So how would you turn _off_ the lombok-generated constructor?

Also, to be consistent, if we take this stance for constructors (generate the constructor unless an explicit constructor with the *Exact* same parameter list exists), then we should also take this stance for all methods generated by lombok (getters, setters, equals, and hashCode, and perhaps more importantly, all future methods we might want to add at some point). That's quite an impact. Because the methods/constructors generated by lombok are far less visible than explicit ones, and overloading with the same # of parameters where each parameter type is equal or one is a subtype of the other is a really bad anti-pattern, so I certainly don't feel entirely comfortable letting lombok actively commit such atrocities and trusting the programmer to 'figure it out'.

I'd see more bread personally in some sort of general lombok annotation that you can stick on any method/constructor that says: It's allright, just generate your stuff, I'll vouch for this method/constructor not conflicting with whatever lombok is planning.


--Reinier Zwitserloot

Tuukka Mustonen

unread,
Mar 26, 2010, 2:26:38 AM3/26/10
to project...@googlegroups.com
On Thu, Mar 25, 2010 at 1:08 AM, Reinier Zwitserloot <rein...@gmail.com> wrote:
So how would you turn _off_ the lombok-generated constructor?

I was supposing you could just turn it off in Lombok's code. No idea how...

Also, to be consistent, if we take this stance for constructors (generate the constructor unless an explicit constructor with the *Exact* same parameter list exists), then we should also take this stance for all methods generated by lombok (getters, setters, equals, and hashCode, and perhaps more importantly, all future methods we might want to add at some point). That's quite an impact. Because the methods/constructors generated by lombok are far less visible than explicit ones, and overloading with the same # of parameters where each parameter type is equal or one is a subtype of the other is a really bad anti-pattern, so I certainly don't feel entirely comfortable letting lombok actively commit such atrocities and trusting the programmer to 'figure it out'.

I'd see more bread personally in some sort of general lombok annotation that you can stick on any method/constructor that says: It's allright, just generate your stuff, I'll vouch for this method/constructor not conflicting with whatever lombok is planning.
 
The annotation approach is alright, I think that's what the @IgnoredByLombok suggestion was after. What I am after is flexibility, I would be ready to sacrifice some simplicity for flexibility. But then, that's just me..

Anyway, I'll gladly leave the decision of right approach and gory details of implementation to you :) Just wanted to share my thoughts and contribute. Thanks for very insightful discussion!

Tuukka

Reinier Zwitserloot

unread,
Mar 26, 2010, 6:55:28 PM3/26/10
to Project Lombok
No, I mean, lombok's @Data should obviously generate a constructor
automatically, but what if you wanted lombok to generate everything
_except_ that constructor? Right now, you define your own constructor
and lombok will automatically choose to not generate it. The same goes
for methods.

On Mar 26, 7:26 am, Tuukka Mustonen <tuukka.musto...@gmail.com> wrote:

> ...
>
> read more »

Maaartin

unread,
Apr 5, 2010, 3:01:24 PM4/5/10
to Project Lombok
On Mar 27, 12:55 am, Reinier Zwitserloot <reini...@gmail.com> wrote:
> No, I mean, lombok's @Data should obviously generate a constructor
> automatically, but what if you wanted lombok to generate everything
> _except_ that constructor? Right now, you define your own constructor
> and lombok will automatically choose to not generate it. The same goes
> for methods.

Probably I'm missing something (although I've just read the whole
thread), but I think this behavior is just fine and should not and
need not change. If there's a @Constructors annotation, then lombok
should generate exactly the constructors specified. In this case there
should be no check for existing constructors, no resolution, etc. So
if you specify
Constructors({noargs=AccessLevel.PROTECTED})
then you always get a protected noargs constructor generated and no
others (regardless of what constructors already exists). As a special
case, using
Constructors({})
could be used to switch the generation off (quite useless since
defining any ctor does it as well).

Maaartin-1

unread,
Jun 29, 2010, 4:52:50 AM6/29/10
to project...@googlegroups.com
There's one more constructor I'd like to have: the clone constructor. By
default it should do a shallow copy of all fields, this is what I need
most of the time. This is usually quite simple, it could handle
primitive types, arrays, and all build-in collections easily. Obviously,
immutable types do not need to be copied.

There should be a possibility to handle other types, e.g., a field
MyFancyCollection myCol;
could be handled by the method cloneMyFancyCollection(MyFancyCollection)
defined in the processed class. The method could be inherited or private
or whatsoever. Alternatively, lombok could look for a method with a name
based on the field name, or on an annotation on the field.

I can imagine to allow sharing of some field, or to require deeper copy
of it, this could be handled by a cloneXXX method, too.

The clone constructor is often quite useful and quite boring to write,
moreover it's easy to forget a field. It's an alternative to the clone()
method and sometimes the only way how to implement it correctly.

What do you think about it?

Reinier Zwitserloot

unread,
Jun 29, 2010, 8:04:50 AM6/29/10
to Project Lombok
Not a bad idea. Unfortunately we can't detect immutable objects, so
we'd have to take some liberties here, and say that clones are
shallow. Folks should be able to figure out from there that this is
irrelevant for immutables. We could take it upon ourselves to roll our
own clones for arrays and stuff from java.util.collections but that
seems a bit arbitrary. We could attempt to deep-clone everything,
requiring either some sort of flag (You may shallow-clone this field)
or a method with a special name like you suggest, for all types that
we don't know how to clone ourselves, but none of this is very
consistent; for example, ArrayList's clone constructor does a
_SHALLOW_ copy; it'll create a new list but the new list is populated
with references to the exact same objects that were in the source
list, even if those objects are Clonable.

The most consistent approach is to say that the whole thing will be
shallow-copied, though this would have to extend to lists. We could
then offer opt-out behaviour: Either annotate a field, or offer a
magically named method, to write your own clone algorithm for a
certain member. Then if you wanted your lists to be semi-shallow (list
is cloned, members aren't), you could write just the "new
ArrayList<T>(oldList)" part of it yourself. Exactly what would this
look like though? Also, with annotations, as they can't contain code,
you could really only annotate Clonables.

Maaartin-1

unread,
Jun 29, 2010, 10:00:12 AM6/29/10
to project...@googlegroups.com
On 10-06-29 14:04, Reinier Zwitserloot wrote:
> Not a bad idea. Unfortunately we can't detect immutable objects, so
> we'd have to take some liberties here, and say that clones are
> shallow.

I mentioned the immutables only as an optimization, as it makes no sense
to clone String, Integer, or BigInteger. But I'd prefer to clone them
to not cloning e.g., arrays (s. below).

> Folks should be able to figure out from there that this is
> irrelevant for immutables. We could take it upon ourselves to roll our
> own clones for arrays and stuff from java.util.collections but that
> seems a bit arbitrary.

Agreed, but a cloned object with shared lists etc. is almost never
usable. Since the whole ctor gets generated, there's no way to fix it
manually afterwards (assuming the fields being final).

> We could attempt to deep-clone everything,
> requiring either some sort of flag (You may shallow-clone this field)
> or a method with a special name like you suggest, for all types that
> we don't know how to clone ourselves, but none of this is very
> consistent; for example, ArrayList's clone constructor does a
> _SHALLOW_ copy; it'll create a new list but the new list is populated
> with references to the exact same objects that were in the source
> list, even if those objects are Clonable.

Yes, but that's a quite different issue. IIRC, I've never needed a
shallow copy and I've never needed an infinitely deep one. Whenever I
cloned an Object I need it to be quite independent of the original in
the sense that calling methods on the copy doesn't influence the
original, but methods should (according to Demeter) only modify the
object properties themselves, not the things contained in them. That's
why depth-1 cloning is nearly always what I needed. OTOH, for
general-purpose containers is shallow copy probably the best. Specifying
the depth in the @Constructor annotation is probably the way to go.

> The most consistent approach is to say that the whole thing will be
> shallow-copied, though this would have to extend to lists.

IMHO, the depth-1 copy is as consistent as the shallow or infinitely
deep one, just more useful for user-defined objects.

I've just had a look at java.util and java.util.concurrent and it looks
like all the collections and maps have a clone constructor.
Unfortunately, the concurrent ones are not Cloneable, and the others'
method clone() returns Object. The clone constructors performs a shallow
copy, which is what you need for containers most of the time.

> We could
> then offer opt-out behaviour: Either annotate a field, or offer a
> magically named method, to write your own clone algorithm for a
> certain member.

I'd prefer specifying the default depth in the class annotation, with
the possibility to override it using member annotation.

Then if you wanted your lists to be semi-shallow (list
> is cloned, members aren't), you could write just the "new
> ArrayList<T>(oldList)" part of it yourself. Exactly what would this
> look like though? Also, with annotations, as they can't contain code,
> you could really only annotate Clonables.

You could select one the following possibilities for object o of class C:
- o
- o.clone()
- new C(o)
- MyCloner.cloneOf(o), with MyCloner.class given as an annotation parameter

Actually, I could be happy with always calling MyCloner, where I could
configure it myself. This could be easy for you to do (at least when
using a string instead of a class as a parameter) and it would be the
most flexible solution. Ideally something like

@CloneConstructor(access=AccessLevel.PRIVATE,
cloneMethod=AccessLevel.PUBLIC, cloner=MyCloner.class)

which would lead to something like

class MyClass {

private Date d;
private String s;
private final List<String> list;

private MyClass(MyClass other) {
d = (Date) MyCloner.cloneOf(other.d);
s = (String) MyCloner.cloneOf(other.s);
list = List<String> MyCloner.cloneOf(other.list);
}

public MyClass clone() {
return new MyClass(this);
}

}

Implementing MyCloner is a trivial exercise for the user. I don't know
how the lombok code gets generated, if it was something like source code
then I'd define special methods for each type, otherwise I'd switch on
the types. Maybe something like follows would be better:

d = MyCloner.cloneOf(Data.class, other.d);

Reinier Zwitserloot

unread,
Jun 29, 2010, 10:06:33 PM6/29/10
to Project Lombok
So the plan is that by default, a clone will create a new object, and
fill it with shallow clones of each field? Does that mesh with what
folks might expect? Could be that's true, in general I program with so
many immutables I don't really notice much. Specifying depth class-
wide seems strange; if I have:

A: Object/MutableType1/Field1
B: Object/MutableType1/Field2
C: Object/MutableType2/Field1
D: Object/MutableType2/Field2

then if I am allowed to mess with how far cloning should go in the
first place, then why not let me say that A should be fully cloned, B
only 2-deep, C and D only 1-deep?

If we call cloner code you write yourself, then I'm not sure lombok
even adds any meaningful boilerplate reduction anymore; might as well
just write your own cloning constructor. I thoroughly dislike magic
signatures; they should only be used as a last resort.
"cloneOf(Type.class, instance)" is definitely a magic signature.
There's just no way for you to know that's how you're supposed to make
them without delving deep into the documentation, and that's really
not a good thing. It can be excusable if its the only sane way to
implement a killer feature, but this isn't a killer feature. I'd have
to ask Roel but for me, this feature is sufficiently borderline that
whatever magic we end up doing should be completely natural and
require next to no configuration. If that's not in the cards then
we're out of luck.

Maaartin-1

unread,
Jun 30, 2010, 5:25:41 AM6/30/10
to project...@googlegroups.com
On 10-06-30 04:06, Reinier Zwitserloot wrote:
> So the plan is that by default, a clone will create a new object, and
> fill it with shallow clones of each field? Does that mesh with what
> folks might expect?

I'd say no, but feel free to introduce a required parameter, so nobody
would be surprised. Or do a shallow copy by default and let them use
their own cloner. This is what I prefer now (see the example below).
Shallow copy is what Object.clone() does so there is no surprise.

> Could be that's true, in general I program with so
> many immutables I don't really notice much.

So do I whenever possible.

> Specifying depth class-
> wide seems strange; if I have:
>
> A: Object/MutableType1/Field1
> B: Object/MutableType1/Field2
> C: Object/MutableType2/Field1
> D: Object/MutableType2/Field2
>
> then if I am allowed to mess with how far cloning should go in the
> first place, then why not let me say that A should be fully cloned, B
> only 2-deep, C and D only 1-deep?

Sure, but this can't be done by a simple class-wide annotation. So you
could allow a global specification in a class-wide template which could
be overridden by field annotations if necessary. I'm going to modify my
example to account for this.

> If we call cloner code you write yourself, then I'm not sure lombok
> even adds any meaningful boilerplate reduction anymore; might as well
> just write your own cloning constructor.

I strongly disagree.

1. In case you need only a shallow copy then your cloner is a class with
single function implementing identity.

2. You could provide some useful cloners solving most common problems. I
fact I've wrote something appropriate for most cases.

3. No matter how complicated you cloner is, you can reuse it more for
many fields in many classes.

Recently, I saw a library containing over 100 classes (implementing
cryptographic hash functions), most of them containing a clone method.
All used fields are either primitives or arrays of them, so only arrays
need to be handled. Writing such a cloner took me less than 5 minutes
and it could save 1000+ lines of code in the library mentioned.

> I thoroughly dislike magic
> signatures; they should only be used as a last resort.
> "cloneOf(Type.class, instance)" is definitely a magic signature.
> There's just no way for you to know that's how you're supposed to make
> them without delving deep into the documentation, and that's really
> not a good thing.

How is it any more magical then any other method? Probably you assume
the method to be static, but it needn't be (see my example below).

The class parameter is not necessary, I left it out for now.

> It can be excusable if its the only sane way to
> implement a killer feature, but this isn't a killer feature. I'd have
> to ask Roel but for me, this feature is sufficiently borderline that
> whatever magic we end up doing should be completely natural and
> require next to no configuration. If that's not in the cards then
> we're out of luck.

I hope you change you mind after having look at my example. The cloning
is a lot of boilerplate and quite error-prone. With the interface I
introduced now the magic is gone IIUYC, the only remaining thing is the
noarg-ctor requirement, but this is quite common.

I created quite a large example containing my new proposal for ctor
annotations which includes static factory methods as well. Because of
this, I'd prefer separate annotations for each constructor. See

http://dl.dropbox.com/u/4971686/lombok/cloner-100630-110303.zip

Maaartin-1

unread,
Jun 30, 2010, 7:35:26 AM6/30/10
to project...@googlegroups.com
I see that the cloning of standard classes is a bit more complicated
than expected, because of the infinite chaos in Java API. Some of the
classes provides a sort of copy constructor, others have a clone()
method returning for whatever reason Object. However, writing a cloner
for the most important cases is easy and doing it for all mutable
classes in say java.lang, java.util, and java.util.concurrent is doable.
Such a cloner could be useful outside of lombok, too.

Customizing the behavior for fields could be easily done using

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface FieldCloner {
Class<? extends Cloner> value();
}

for overriding the default cloner.

Reinier Zwitserloot

unread,
Jun 30, 2010, 11:14:06 AM6/30/10
to Project Lombok
It's magic in that, unlike a normal method, _or_ abstract methods
listed in an interface, you have no idea what you're supposed to
implement without looking at javadoc. the main method is an example of
a magic method: Unless someone tells you that's what you should write,
there's no hint. If java were redesigned today I'm fairly sure we'd
have an interface (java.lang.Application or some such), a requirement
to have a no-args constructor, and you can run any subtype of
Application. Serialization is also rife with magic signatures; the
serialVersionUID field as well as the magic signatures for readObject
and writeObject. These things invariably show up in "how not to write
APIs" examples, for good reason.

If we're going to support this I'm strongly leaning towards the
following concept:

The annotation to generate a cloning constructor itself will by
default create a new object and assign to each field the cloned
version of each current variable. I complained before that this
doesn't appear to be particularly expectable, but now I'm having
second thoughts on that concern; in general this always seems to be
'the right thing'. ArrayList's cloner will for example copy all
internal primitives (length and size), as well as the backing array,
but not the elements in it. That's exactly this: Create a new
ArrayList and assign to each field in it a clone of the original.
We'll of course be smart enough to know that there's no need to clone
BigInteger, BigDecimal, java.lang.Integer and wrapper friends, all
primitives, and Strings. arrays and lists are already cloneable so
that works out nicely. If any of your fields aren't cloneables, the
annotation will generate a compile time error. Possibly there will be
another annotation you can stick on a field to say that this
particular field does not need cloning, and possibly there will be a
flag on the constructor annotation to say you want a completely
shallow clone.

If the above cannot be used to arrive at the desired constructor, then
you'd have to write it from scratch. I get the feeling the above
should cover easily 80%+ of all required copy constructors, and adding
loads of complex interactions including either magic signatures or
extra interfaces (which would also lead to runtime dependencies,
something we try to avoid), does not "feel" like lombok. We tackle
boilerplate that is both (A) annoying and hard to get right, and (B)
common. Once some annoying and hard to get right boilerplate ceases to
be particularly common, it's out of lombok's current aims. We've got
more pressing boilerplate to fry :P

While the right way to clone an arraylist is normally: new
ArrayList<T>(otherList);, you can also clone it with:
(ArrayList<T>)otherList.clone(); - this is nice and general.

We can't do the above plan yet, though - in order to know that
"ArrayList" is in fact referring to "java.util.ArrayList" we'd
preferably have full resolution. Right now we have lightweight
resolution, based on our own interpreting of the import list, but it's
not perfect. Also, it is completely incapable of figuring out if any
given class is Cloneable, so if we build it with today's lombok we
could only insert a runtime instanceof check.