Thanks for your answer,
Sorry for taking a short break, I tried much code and had many things
to do last days. And I wanted to provide an usefull answer, replies
inline.
> > - You should be carefull about arrays and collections. Creating a
>
> > public void setFoo(Foo[] foo) { this.foo = foo; }
>
> returning an immutable view would be a surprise. That brings lombok
> firmly into the too much magic camp, I think. I'm empathetic to the
> issue; after all, if you want to have an immutable object that
> contains a list, it won't be, if people construct them with just your
> average (non-Collections.unmodifiableList()-protected) ArrayList.
>
> However, there are a whole host of issues in automating this in
> lombok:
> (1) - arrays: Screw em.
Agree. Sometimes even arrays are usefull, e.g. byte[], but normally
those must be exposed...
>
> (2) - collections API: We'd have to hard code the rules in. I don't
> like that. What if people prefer javolution?
Any other collection API transforms with the usual rules to getters/
setters, but as you wrote:
> (it's java.util! It's allowed to be special), but I'm more worried
> about the magic factor: How can one know that java.util.List, Set, and
> Maps are protected via Collections.unmodifiable*, but everything else
> isn't? You wouldn't know unless you sift through the docs. That's not
> good.
Yes, you're right. The developer needs a way to declare the behavoir
somewhere, if desired. A simple solution would perhaps be an @Getter
attribute
@interface Getter {
//...
boolean immutable() default false;
}
- For java.util.* special implementation, e.g.
@Getter(immutable=true)
private List<String> unexposed;
public List<String> getUnexposed() {
return Collections.unmodifiableList(unexposed);
}
- Would not allow setters to be created
- For primitives no additional action taken
- Else return a deep clone at the getter method
This shouldn't be too magic and is easily describeable at the docs/
examples and would extend to a more flexible usage for getters. What
do you think?
>
> > - Adding a @Has annotaion to a field foo would create a
>
> I'm not convinced. Lombok aims to eliminate boilerplate. It's not
> designed to become a new programming language. If you want a hasFoo()
> method, just add it to the code. It's a one-liner, not too bad.
Yeah. Ok, as it's a oneliner and not standard for APIs and such,
forget about this one.
> > @EqualsAndHashCode
> ...
> Once lombok gets the ability to do type introspection, it can detect
> if the parent's equals and hashCode implementations are generated by
> lombok (and therefore, totally safe for chaining). The default action
> can then be to call super and not to emit that warning.
Ok, looking forward to it. As I noticed but not read yet, there is a
discussion on track about this. So I may participate there.
> The original idea behind ToString is that the printed string is all
> you need to construct a new one, provided you stick 'new' in front of
> it. Which doesn't really hold up - strings aren't even escaped and
> quoted. So, I think you're right about this. It could be smarter about
> it and only default to includeFieldNames if you have more than 1 or 2,
> but then we get back to the 'too much magic' problem. So, let's go
> with includeFieldNames true by default, no matter how many fields you
> have. Can you file a ticket for this? Thanks.
I'll do after posting the answer.
> > @Data
> > - Transient Fields are not excluded ...
> > - As an alternative introduce @NoSetter/@NoGetter
>
> Eh. Feels like too many annotations to me. I could extend AccessLevel
> to include .DONTGENERATE but that's even worse (nothing is not an
> access level, of course). It's a problem though. Is there a way out of
> this that's nicer? Perhaps add exclude="" to @Data itself. The reason
> lombok doesn't cater to these situations is that it's not what lombok
> was for. Any class with transient fields is probably well beyond the
> level of 'trivial little structy class' which is what @Data was meant
> for. So I'm not convinced lombok needs to cater to this case. How
> often do you use transient fields? I can count on one hand how often
> I've used them, and in all cases, @Data wouldn't really fit for that
> class.
BTW, working with EJBs and JaxB I have transient fields quite often.
Not that much but I need them now or then. But the point is: When
using @Data my classes often have only a getter or a setter for a
field. Especially for Lists/Maps, etc. the above mentioned 'immutable'
attribute would be enough, but for any other field type I need a way
to avoid the creation of a setter/getter. I really think, Lombok
should handle this case. But I agree with you:
- Adding an exclude list on @Data would not be enough, because you
would need a list for getters and for setter. Much typing. Bad.
- Extending AccessLevel is bad also, I agree.
- @NoSetter/@NoGetter, which are additional annotaion with no other
value left. Also bad.
- Transient fields are neither a good way, although lombok already
reacts on them when using @EqualsAndHashCode. But extending this to
@Getter/@Setter would prevent Setter/Getter method creation and
normaly transient fields need access methods also.
Conclusion: Bad. What's left then?
Changing the behaviour of @Datas Setter/Getter creation would be an
option:
- Create Setter and Getter if neither @Setter nor @Getter is annotated
at a field or @Setter and @Getter are both anotated
- Create only Setter if @Setter is annotated
- Create only Getter if @Getter is annotated
In other words: create only the method type that is declared, but
default to both annotations declared, if not annotated. No additional
clutter but again, the behavoir is simple declared by the annoations
the developer writes down. I think this can be considered a logical
behavior in my eyes and easy to understand.
Would this solve the problem?
> > @Synchronized
> ...
> Is there an advantage to doing that, though? Once lombok gains the
> ability to introspect on types, we could detect that you're trying to
> synchronize on a lock, and lock the lock instead.
Yes there are multiple advantages for using a ReentrantLock over an
object array and synchronized:
- The acquireing of the lock can have a timeout: tryLock(long time,
TimeUnit unit);
- If the developer knows the name of the lock variable (he can declare
the lock field or use the variable created) he is able to use
Conditions for Consumer/Producer like problems and all other sort of
notify scenarios (see
http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/Condition.html).
- With fair=true the order of the waiting threads is preserved when
executed after release of the lock
- A synchronize on ReentrantReadWriteLock can be used to synchronize a
method explicitly for read or write thus speeding up parallel access
Well but I see... packing this all into different annotiotions is
huge. Well, lombok could again use an attribute:
@interface Synchronized {
//...
LockBehavior lock() default LockBehavior.SYNCHRONIZED;
}
so the original way kicks in. Additional enums would be:
- REENTRANT: Use a ReentrantLock, usefull if using conditions
- FAIR: Use a ReentrantLock with fair=true, usefull if using
conditions
- TRY: Only try to obtain lock, use tryLock() method
- TIMEOUT: which would need additional attributes for the time and
unit though
- READ: use ReentrantReadWriteLock().readLock()
- WRITE: use ReentrantReadWriteLock().writeLock()
- THIS: synchronized(this) instead of synchronized($lock)
Well, I also found negatives by google:
http://book.javanb.com/java-concurrency-in-Practice/ch13lev1sec4.html
(Java Concurrency in Action, Chapter 13.4)
You decide... But I think using Locking over synchronized is supirior
because of the additionally options for the developer, even better
with the extended annotation
> > More ideas:
>
> > - Seperate @Builder and @Fluent, so that @Fluent can be used at any
> ...
> If there are no suitable answers, then lombok should just pick as best
> an answer as it can and get a @Builder out there, but we can
> deliberate on exactly how to roll with it.
>
As for @Builder: I need to read more messages from the board and some
prototyping before proposing feedback or an implementation. But I
wanted to concentrate on @Fluent here as an independant Annotation.
@Builder would only incorparate @Fluid like @Data includes @Setter/
@Getter for example. If @Fluent itself would include @Getter and
@Setter, the main advantage is usability for every class out of the
box:
@Fluent
class Example {
String name;
public void foo() { System.out.print("foo"); }
}
transforms to:
class Example {
String name;
public Example foo() { System.out.print("foo"); return this; }
public String getName() { return name; }
public void setName(String name) {
this.name = name; }
public String name() { return this.getName(); }
@SuppressWarning("hiding")
public Example name(String name) { this.setName(name); return this; }
}
Would preserve original setter/getter syntax which could be important
for reflection, etc. I know more issues will arise about naming, deep
nesting, overwritten methods and such, but I think it's
worth it and @Builder could automaticly reuse this functionality.
> > - What about validation?
> ...
> NotNull already exists in the git trunk, which triggers off of any
> annotation named NotNull and NonNull (there are a bunch floating out
> there. Hopefully they'll all coalesce on JSR305's annotations).
You may consider to remove @NotNull @NonNull from the release again,
if JSR303 compatibility is reached?
> such a system sounds like quite an undertaking. I'm not sure the
> burden of fielding a complex validation framework is really going to
> help make your code more readable and less boilerplate-y. Is this:
Yes, it makes it declerative, thus readable and understandable for
everone looking at the fields, not at the implementation of methods.
There are also less tests to write, if automatic validation is
provided by lombok on which developers and tester can rely. I'm
currently working on possibilities to achieve a flexible yet clear and
easy to use way for JSR303 support while respecting different
contexts and requirements from the developers point of view. I think
I'll file in an issue when requirements are clear.
> The answer is of course: Yes, it's more readable, and less
> boilerplatey, but by how much of a factor, and at what cost? There are
> the usual slate of open questions, too: Should this validation system
> be pluggable? Should, in fact, the entirety of @Data/@Setter/@Getter
> be pluggable (should it auto-discover template generators and call
> into them to generate the setter and getter given the annotations on
> the type and the field in question?)
Yes, I think a plugable solution would be best to encourage develpers
to code their own annotations and transformations. Also other APIs
would be able to support Lombok with own annotations and create
boilerplate code for the developer. Plus many internal calls can be
made plugable too, I think although I'm not into code yet. The point
is, if thinking of transformations in a form of aspects, a plugable
system would fit perfectly. Combining transformation with aspect gives
a @Tanspect. Using a similar system like defining Validators for
JSR303 you may come up with something like the follwing classes, which
are i simplified idea:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Transpect {
Class<? extends TranspectInterceptor<?>>[] interceptedBy();
Class<? extends Annotation> annotation() default Transpect.class;
}
public interface TranspectInterceptor<T extends Annotation> {
public void transform(List<TransformationPoint<T>> points,
TransformationContext context);
}
r
Along with some other classes this would basicly allow to define an
interception fo the transfomation process for one annotation and which
class to process the transpect: Using this for my infamous
@Has annotation I would write something like this:
@Transpect(annotation = Getter.class, interceptedBy =
HasTransformer.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Has {
AccessLevel accessLevel() default AccessLevel.PUBLIC;
}
public static class HasTransformer implements
TranspectInterceptor<Has> {
@Override
public void transform(List<TransformationPoint<Has>> points,
TransformationContext context) {
TransformationClass target = context.targetClass();
for (TransformationPoint<Has> point : points) {
switch (point.elementType()) {
case FIELD:
Field field = point.field();
Has annotation = point.transpect();
createHas(target, field, annotation);
break;
default:
}
}
}
private void createHas(TransformationClass target, Field field, Has
annotation) {
// TODO: respect accessLevel()
String method = "public has$upperName() { return $name != null; }";
String name = field.getName();
String firstUppercaseName = name.substring(0, 1).toUpperCase() +
name.substring(1);
method = method.replace("$upperName", firstUppercaseName).replace
("$name", name);
target.addMethod(method);
}
}
@Data
public static class Foo {
@Has
private String name = null;
}
// --- transforms to
public static class FooVanilla {
private String name = null;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean hasName() { return name != null; };
}
This is just a vast exmaple of how I think a plugable system could
work. It's surely much too short-sighted but I think you're getting
the idea.
> > enforce my own validator to kick in. But wait... There is already a
> > reference implementation by the Hibernate team for the Bean Validation
> > Framework JSR 303 (
https://www.hibernate.org/412.html). So why not
> > incorparate this and make validation a non-typer for the developer:
>
> Yeah, I was thinking of that too. Either lombok becomes pluggable, or
> lombok just becomes JSR 303 compatible. It's a good idea. Feel free to
> file an issue for it, but give it priority low - as cool as this is,
> it can't hold a candle to @Delegate and some of the other ideas in the
> pipeline, I think. Or, better yet, get hacking on this yourself! I'll
> accept a patch that's JSR 303 compatible.
I'll do... At least I'll try, I'll file in an issue as written above
and then I'm going the get this done simple, easy and clear. But: This
is my first open source project I want to participate further. Can you
give me some advise about do's and don'ts and where to start with git,
where information on the build process is, etc... Is there a
developers faq for contributors or a how to start for dummies :-) out
there?
Greetz,
GHad