@Value, and defaulting fields to private final.

2,008 views
Skip to first unread message

Reinier Zwitserloot

unread,
Jul 23, 2012, 12:24:28 AM7/23/12
to project...@googlegroups.com
Inspired by another recent thread, it makes some sense that for @Value (which is just like @Data, but is designed for immutables; instead of setters, you get withers), all fields should default to private final. So, you could write:

@Value public class Person {
    String name;
    int age;
}

In order to facilitate this, we were thinking of adding an annotation named @DefaultFieldAccess which requires an access level, and optionally a value for 'setFinal'. This will set every field that has package private access to this access level, and if setFinal is true, add final to any field that isn't already final. To allow opting out of this mechanism, just specify your own access level (i.e.e we won't mess with 'public int age;'. Package private and non-final have no keywords, so we're making keywords: @PackagePrivate and @NonFinal.

@Value then becomes shorthand for:

@ToString @EqualsAndHashCode @Wither @Getter @AllArgsConstructor @DefaultFieldAccess(value = AccessLevel.PRIVATE, setFinal = true)


comments?

NB: We could make it such that any field with @Setter on it is automatically treated as @NonFinal, but that's a little too much magic. There's already a scary amount of inter-dependencies between the various @Data-related handlers, and we don't want to go overboard on them. We'll require @NonFinal to be used together with @Setter where relevant.

eric.giese

unread,
Jul 24, 2012, 1:16:01 AM7/24/12
to Project Lombok
I would be a bit surprised to see that @Value is not just an @Data
with Withers, but also has special semantics when it comes to field
visibility. Setting them automatically to final as the default feels
natural for Values, but the modification of the visibility feels not.

I'm kind of biased: On the one hand, this feature is not that hard to
grasp, so acceptance should not be a problem. But I also do not see
that much added value by not typing two additional words per field
which everybody expects anyway.

@Value would still work if the fields are non-final, since it does not
offer any mutators, and package-private is still a modifier with a
closed scope, so there is not really a necessity for this step IMHO.

Maaartin G

unread,
Jul 24, 2012, 7:20:12 PM7/24/12
to project...@googlegroups.com
On Tuesday, July 24, 2012 4:29:44 PM UTC+2, joseaio wrote:

Hello Reinier (I'm new on Lombok and Forums)

  I would like to known, Why @Getter, @Setter, @DefaultFieldAccess uses AccessLevel instead of java.lang.reflect.Modifier?
 
  This alternative allows any modifier on annotations, please see next example:

    @Getter(Modifier.FINAL | Modifier.PROTECTED)   // defined as int
    @Getter({Modifier.FINAL,Modifier.PROTECTED})  /defined as int []

I think I can answer this part too:
1. There's no `Modifier` corresponding with `AccessLevel.DEFAULT`, so you could not state it explicitly.
2. The default e.g. for `@Getter` must be `public`, which doesn't correspond with leaving all modifiers out.
3. I would also allow

@Getter({Modifier.PUBLIC | Modifier.PRIVATE, Modifier.INTERFACE ^ Modifier.STRICT - Modifier.NATIVE * 42})

4. Any such bit-fiddling is ugly and `Modifier` even more than necessary.

Reinier Zwitserloot

unread,
Jul 29, 2012, 10:24:53 AM7/29/12
to project...@googlegroups.com, eric...@googlemail.com
On Tuesday, July 24, 2012 7:16:01 AM UTC+2, eric.giese wrote:
I'm kind of biased: On the one hand, this feature is not that hard to
grasp, so acceptance should not be a problem. But I also do not see
that much added value by not typing two additional words per field
which everybody expects anyway.

I have the same reaction to this proposal, but when I have to defend this point of view rationally I just can't do it. Yes, those 2 words don't seem to add up, but why does it seem like that? It's 2 words. It's utter and complete boilerplate - it adds absolutely nothing. Of course every field in a @Value-style class is private (if we're generating getters and withers, why make em public? Speed reasons? That seems like a pretty bad reason to do that). This is why I'm strongly considering releasing this feature with the default-field-access feature in place. If we get lots of whines on the topic we can revisit it when / if @Value is promoted from .experimental to main package.
 

@Value would still work if the fields are non-final, since it does not
offer any mutators, and package-private is still a modifier with a
closed scope, so there is not really a necessity for this step IMHO.

Right, every so often you WANT package private access to your inner fields. But why, really? It's less consistent and a modern hotspot compiler eliminates the method call.
 

Reinier Zwitserloot

unread,
Jul 29, 2012, 10:28:00 AM7/29/12
to project...@googlegroups.com
Using a JavaBean interface is not feasible primarily because doing any post-resolution transformation is a lot more work and a lot less stable. @Data and friends work on just the raw AST; it does not need to know _ANYTHING_ about the source/classpath of the project that the source file is in. If we need to do this for any class that inherits from JavaBean, we need to answer the question: "What are the supertypes of this class", and that requires resolution.

Also, while it's not a complete showstopper, 'invisible magic' is a big negative in our book, especially in this case: If you make a class 'javabean', then you expect all subclasses to get the lombok treatment. But, if someone else takes your class and writes their own thing, and then compiles it, _WITHOUT LOMBOK!!_, then that code will still compile just fine but it will not have received the lombok treatment. This is a very bad scenario (in that it results in surprised programmers and that's never good), and quite likely.

Also, that means that lombok.JavaBean is a runtime dependency. Lombok currently has no runtime dependencies, and this is a good thing.

The costs are therefore very large, and the benefit is very tiny. Not going to happen.


On Tuesday, July 24, 2012 4:29:44 PM UTC+2, joseaio wrote:

Hello Reinier (I'm new on Lombok and Forums)

  I would like to known, Why @Getter, @Setter, @DefaultFieldAccess uses AccessLevel instead of java.lang.reflect.Modifier?

  This alternative allows any modifier on annotations, please see next example:

    @Getter(Modifier.FINAL | Modifier.PROTECTED)   // defined as int
    @Getter({Modifier.FINAL,Modifier.PROTECTED})  /defined as int []

Another interested feature, for me, must be an empty interface @JavaBean

   This is similar to @Data but inject code on every hierarchy (any class that implements JavaBean or any subclass that extends, directly or indirectly from a class that implements JavaBean)

I am really impressed by your work on lombok...

Mike Power

unread,
Jul 31, 2012, 1:32:06 AM7/31/12
to project...@googlegroups.com
I am wondering if this would be less interesting for the class creator
then for the class consumer. Very similar to the old const usage in
C++. If I am given an instance that I know is a value object I can feel
more confident passing it around to other clients. But if I cannot be
sure it is a value object and I pass it on to clients they may change
the value of the object.

Without lombok I might create Value interface for compile time type
checking:
public interface Value <T> {
}

Then when I want to declare a value class I simply implement that interface

@Data
@RequiredArgsConstructor
public class Point implements Value<Point> {
private final int x;
private final int y;
}

Now I could write a class that uses Point like Area and at compile time
guarantee that Point is a Value object:
public class Area {

public static <T extends Point & Value<Point>> int area(T
lowerLeft, T upperRight) {
return upperRight.getX() - lowerLeft.getX() * upperRight.getY()
- lowerLeft.getY();
}
}

This is really a toy example area has no need to ensure that its
parameters are value objects...

Perhaps a more concrete example of unmodifiableList. As much as you can
call unmodifiableList and get a list that cannot be modified nothing
guarantees you that the contents of the list itself will not be
modified. But with this new interface I can type the method to take in
only value objects.

public class Collections {
public static <T extends Value<T>> List<T> unmodifiableList(T...
points) {
return
java.util.Collections.unmodifiableList(Arrays.asList(points));
}
}

The problem is the marker interface value is only a suggestion that the
type I created is a value type. Nothing stops me from writing:
@Data
@AllArgsConstructor
public class Point implements Value<Point> {

private int x;
private int y;
}

Whoops...

I doubt this is possible from what threads I have followed but I'll
propose it anyway.

It would be great if we could type the value interface like so
@lombok.RequireValue
public interface Value <T> {
}

Then when any object attempts to implement Value lombok introduces a
compiler error via RequireValue annotation, should that object provide
setters to its fields. Then I guess you might have justification in an
annotation like @Value:
@Value
public class Point {

private int x;
private int y;
}

eric.giese

unread,
Aug 5, 2012, 4:39:18 AM8/5/12
to Project Lombok

> I have the same reaction to this proposal, but when I have to defend this
> point of view rationally I just can't do it. Yes, those 2 words don't seem
> to add up, but why does it seem like that? It's 2 words. It's utter and
> complete boilerplate - it adds absolutely nothing. Of course every field in
> a @Value-style class is private (if we're generating getters and withers,
> why make em public? Speed reasons? That seems like a pretty bad reason to
> do that). This is why I'm strongly considering releasing this feature with
> the default-field-access feature in place. If we get lots of whines on the
> topic we can revisit it when / if @Value is promoted from .experimental to
> main package.
>

Of course, it is boileplate. I am only a bit concerned since this
behavior differs from @Data when it comes to field visibility.

If data had got this right in the first place, this would seem
completely logical.
But having 2 different approaches to visibility seems odd.

However, i completely agree on adding the final modifier, since that
does not break the symmetry, as values are expected to be immutable
anyway.


> Right, every so often you WANT package private access to your inner fields.
> But why, really? It's less consistent and a modern hotspot compiler
> eliminates the method call.

I just gave this example as why package visibily is not a problem if
you forgot to add final manually.
Its still a closed, controllable scope.
And visibilty on final fields usually does not pose any kind of
problems.

Thats why I do not care that much about setting them automatically to
private.

Reinier Zwitserloot

unread,
Aug 11, 2012, 12:18:14 PM8/11/12
to project...@googlegroups.com, eric...@googlemail.com
We're about to roll out an update. We have @FieldDefaults which is the part of @Value that handles this. @Value is now shorthand for:

final @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) @EqualsAndHashCode @ToString @AllArgsConstructor @Wither @Getter

Making the class itself final also seemed like a good plan, because these things are (normally) immutable, and a subclass cannot be forced to also be immutable.

We also added annotations @NonFinal and @PackagePrivate to override these things (i.e. you can annotate your class @NonFinal @Value if you don't want @Value to make your class final).

This is a pretty big test of the experimental concept - feedback definitely desired on this. Further announcement will follow pretty soon (probably mondayevening).
Reply all
Reply to author
Forward
0 new messages