简体   繁体   中英

Will @NonNull give an error on using @NoArgsConstructor?

@Getter
@Setter
@NoArgsConstructor
public class Company {

    @NonNull
    private String location;
    
    @NonNull
    private String name;
}

Now if I write something like -

  1. Company company = new Company();
  2. company.setLocation("Florida");
  3. company.setName("ABC");

Question 1 - On line 1 would I get an error, as a new object is created but location and name are null and they are annotated with @NonNull ?

Question 2 - Would I get any errors on lines 2 and 3?

It's complicated, because the java community deals with null in different ways, and 'non null' means different things: For example, in a DB model class it can mean: The CREATE TABLE statement that the DB framework generates if you ask it to make the tables for you should add a NOT NULL constraint in the SQL. However, as the POJO is also supposed to be capable of representing a half-built row (for example, where the unid field, which represents the unid column in your database, holds null because the whole point is to let the DB use its auto-sequence to fill it in, which won't happen until you save() it, therefore java-side that field can and usually is null!

Thus, lombok's NonNull support has 4 aspects to it which are all very very different:

@RequiredArgsConstructor and @AllArgsConstructor , as well as all lombok features that imply one of those ( @Builder , @Data , and @Value ).

Lombok treats any field that is either final or has some sort of NonNull annotation that it recognizes specifically as implying that no object should ever even exist where this field is null (which notably excludes JPA's NotNull!), and which is configurable in lombok.config but lombok ships with a decent known set of annotations out of the box, as 'required'.

A 'required' field has the following properties:

  1. @RequiredArgsConstructor will make a parameter for it.
  2. The constructors made by @RequiredArgsConstructor and @AllArgsConstructor will null-check these fields using the nullcheck mechanism as configured in your lombok.config. By default, it's: if (x == null) throw new NullPointerException("x"); .

@NoArgsConstructor

This one is built different. NoArgsConstructor adds no null checks . It would be completely pointless: If lombok was supposed to add them, then that means the only right answer is that lombok makes this constructor for you:

public YourType() {
    throw new NullPointerException("NameOfFirstFieldMarkedNonNull");
}

which seems like a pointless exercise.

So, instead, lombok leaves those fields as null and assumes you know what you are doing. Generally you make these constructors either because a framework requires that you do so, or because a framework will 'fix' the problem of having fields marked as never null containing a null value by immediately invoking a bunch of setters or reflectively updating those fields.

@Setter as well as lombok features that imply it ( @Data ).

These will null-check the argument in the same way.

@lombok.NonNull on a parameter

Unlike the previous stuff, this feature works ONLY for lombok.NonNull and not for any other non-null annotations: This will add a nullcheck at the top of your method.

The reason lombok doesn't do this for others is that it's conflicting: If you consider @NonNull as a type annotation, then it's saying: This value cannot possibly be null, it's guaranteed, which inverses the nature of it: Now a null check is a compiler error/warning!

It's like writing:

public void foo(String x) {
    if (x != null && !(x instanceof String)) throw new IllegalArgumentException("x is not a string");
}

That should be a compiler error/warning as it can't happen and is just useless code. If you work in a very strict nullity system that is ruthlessly checked by your compiler, @NonNull can imply the same thing, thus, we don't just add that null check.

@Builder

Yes, that will throw NPE. That's because @Builder on a class is just syntax sugar for: @AllArgsConstructor (which will throw NPEs if a parameter is null for a field that is marked NonNull), and then put @Builder on that all-args constructor. So, the builder doesn't null check, but the builder's .build() call will call that constructor, and it will.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM