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:
...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?
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...
--
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
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.
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
--
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?
What about creating setters in such a way that:
public MyObject setSecond(Integer second) {
this.second = second;
return this;
}
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
@Constructor({"this", "that", "theother"})
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>
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.
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...
--
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.
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>
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.
@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.
@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.
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:
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>
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
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.
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.
On Mar 26, 7:26 am, Tuukka Mustonen <tuukka.musto...@gmail.com> wrote:
> ...
>
> read more »
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).
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?
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);
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
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.