Project Lombok in Java
Krzysztof Czajkowski
krzysztof.czajkowski@softwarehut.com
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
private LocalDateTime birthDate;
private List<Superhero> friends;
private List<Superhero> enemies;
}
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
private LocalDateTime birthDate;
private List<Superhero> friends;
private List<Superhero> enemies;
public Superhero() {
}
}
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
private LocalDateTime birthDate;
private List<Superhero> friends;
private List<Superhero> enemies;
private City birthPlace;
private Planet homePlanet;
private City residenceCity;
private Nature nature;
}
Can we do it easier?
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
private LocalDateTime birthDate;
private List<Superhero> friends;
private List<Superhero> enemies;
}
@Data
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
private LocalDateTime birthDate;
private List<Superhero> friends;
private List<Superhero> enemies;
}
@Data
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
private LocalDateTime birthDate;
private List<Superhero> friends;
private List<Superhero> enemies;
private City birthPlace;
private Planet homePlanet;
private City residenceCity;
private Nature nature;
}
How to add Lombok to your project?
Maven
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.6</version>
</dependency>
Gradle
Version < 2.12
dependencies {
provided "org.projectlombok:lombok:1.16.16"
}
Version >= 2.12
dependencies {
compileOnly "org.projectlombok:lombok:1.16.16"
}
Annotation processing
lombok.core.AnnotationProcessor extends AbstractProcessor
Getter and Setter
Getter and Setter
@Getter
@Setter
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
}
Getter and Setter
@Getter
public class Superhero {
@Setter
private String firstName;
@Setter
private String lastName;
@Setter
private String name;
private String superpower;
}
NonNull
NonNull
@Getter
@Setter
public class Superhero {
private String firstName;
private String lastName;
private String name;
@NonNull
private String superpower;
}
NonNull
public void setSuperpower(@NonNull String superpower) {
if(superpower == null) {
throw new NullPointerException("superpower");
} else {
this.superpower = superpower;
}
}
EqualsAndHashCode
@EqualsAndHashCode
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
}
EqualsAndHashCode
@EqualsAndHashCode(of = {"name"})
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
}
EqualsAndHashCode
public int hashCode() {
boolean PRIME = true;
byte result = 1;
String $name = this.name;
int result1 = result * 59 + ($name ==
null?43:$name.hashCode());
return result1;
}
EqualsAndHashCode
@EqualsAndHashCode(exclude = {„superpower"})
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
}
EqualsAndHashCodepublic int hashCode() {
boolean PRIME = true;
byte result = 1;
String $firstName = this.firstName;
int result1 = result * 59 + ($firstName ==
null?43:$firstName.hashCode());
String $lastName = this.lastName;
result1 = result1 * 59 + ($lastName ==
null?43:$lastName.hashCode());
String $name = this.name;
result1 = result1 * 59 + ($name ==
null?43:$name.hashCode());
return result1;
ToString
@ToString(of = {"name", "superpower"})
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
}
ToString
public String toString() {
return "Superhero(name=" + this.name +
", superpower=" + this.superpower + ")";
}
Generate constructors
NoArgsConstructor
NoArgsConstructor
@NoArgsConstructor
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
}
AllArgsConstructor
AllArgsConstructor
@AllArgsConstructor
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
}
AllArgsConstructor
@ConstructorProperties({"firstName", "lastName",
"name", "superpower"})
public Superhero(String firstName, String lastName,
String name, String superpower) {
this.firstName = firstName;
this.lastName = lastName;
this.name = name;
this.superpower = superpower;
}
RequiredArgsConstructor
RequiredArgsConstructor
@RequiredArgsConstructor
public class Superhero {
private final int counter;
@NonNull
private String firstName;
private String lastName;
private String name;
private String superpower;
}
RequiredArgsConstructor
@ConstructorProperties({"counter", "firstName"})
public Superhero(int counter, @NonNull String
firstName) {
if(firstName == null) {
throw new NullPointerException("firstName");
} else {
this.counter = counter;
this.firstName = firstName;
}
}
Data
@Data
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
}
Combined annotations
@RequiredArgsConstructor @ToString
@Getter
public class Superhero {
@Setter
private String firstName;
@Setter
private String lastName;
@NonNull @Setter
private String name;
private String superpower;
}
Log
@Log
public class Superhero {
public void doSomething() {
log.log(Level.INFO, "Doing something");
}
}
Log
public class Superhero {
private static final Logger log =
Logger.getLogger(Superhero.class.getName());
public Superhero() {
}
public void doSomething() {
log.log(Level.INFO, "Doing something");
}
}
Log
maj 31, 2017 8:55:24 AM com.sh.lombok.superhero.model.Superhero
doSomething
INFO: Doing something
Builder
@Builder
@ToString
public class Superhero {
private String firstName;
private String lastName;
private String name;
private String superpower;
}
Builder
public static void main(String[] args) {
Superhero batman = Superhero.builder()
.firstName("Bruce")
.lastName("Wayne")
.name("Batman")
.superpower("")
.build();
log.log(Level.INFO, batman.toString());
}
Builder
INFO: Superhero(firstName=Bruce, lastName=Wayne, name=Batman,
superpower=)
Delombok
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.16.16.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>delombok</goal>
</goals>
</execution>
</executions>
</plugin>
Delombok
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public Nature getNature() {
return this.nature;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public void setFirstName(final String firstName) {
this.firstName = firstName;
}
Pros
•Less code
•More readable classes
•Faster development
Cons?
•Can’t detect constructor of superclass
•Using non-public API - upgrading
compiler may break your code
Project Lombok
• https://projectlombok.org/
The End

jSession #3 - Krzysztof Czajkowski - Lombok in Java

Editor's Notes

  • #16 W jaki sposób dodac Lomboka do projektu? Wystarczy dodać dependency do pom.xml i już, cała magia jest zrobiona, maven ściągnie odpowiedniego jara i można spokojnie używać w projekcie.
  • #17 Gdy projekt jest budowany przy użyciu Gradle, również można dodać do niego Lomboka. W zależności od wersji: Gradle 2.12 lub nowszy -> w pliku build.gradle w sekcji dependencies umieszczamy: compileOnly Gradle starszy -> w pliku build.gradle w sekcji dependencies umieszczamy: provided
  • #18  Annotation processing to narzęcie wbudowane w kompilator javy do skanowania i przetwarzania adnotacji podczas kompilacji. Można zarejestrować własny procesor adnotacji dla konkretnych adnotacji Procesor adnotacji dla konkretnych adnotacji bierze kod javy (lub byte code) i na jego podstawie generuje pliki (zazwyczaj *.java). Co to znaczy? Pozwala to generować kod. Wygenerowany kod jest w wygenerowanym pliku *.java. Oznacza to, że nie można zmieniać istniejącej klasy poprzez dodanie metody. Wygenerowany plik java będzie skompilowany przez kompilator (javac) jak każdy inny plik napisany ręcznie. Annotation processing is a tool build in javac for scanning and processing annotations at compile time. You can register your own annotation processor for certain annotations. An annotation processor for a certain annotation takes java code (or compiled byte code) as input and generate files (usually .java files) as output. What does that exactly means? You can generate java code! The generated java code is in a generated .java file. So you can not manipulate an existing java class for instance adding a method. The generated java file will be compiled by javac as any other hand written java source file.
  • #19 Adnotacje Getter i Setter służą do generowanie getterów i seterów, czyli to, gdzie możemy zaoszczędzić szczególnie dużo kodu. Adnotacje mogą być dodane na poziomie klasy, wtedy metody są generowane dla wszystkich pól, albo można wygenerować te metody tylko dla konkretnych pól.
  • #20 Adnotacje Getter i Setter służą do generowanie getterów i seterów, czyli to, gdzie możemy zaoszczędzić szczególnie dużo kodu. Adnotacje mogą być dodane na poziomie klasy, wtedy metody są generowane dla wszystkich pól, albo można wygenerować te metody tylko dla konkretnych pól.
  • #21 Adnotacje Getter i Setter służą do generowanie getterów i seterów, czyli to, gdzie możemy zaoszczędzić szczególnie dużo kodu. Adnotacje mogą być dodane na poziomie klasy, wtedy metody są generowane dla wszystkich pól, albo można wygenerować te metody tylko dla konkretnych pól.
  • #25 Generuje metody equals() i hashCode().
  • #26 Generuje metody equals() i hashCode().
  • #27 Generuje metody equals() i hashCode().
  • #28 Generuje metody equals() i hashCode().
  • #29 Generuje metody equals() i hashCode().
  • #30 Proste generacja metody toString().
  • #31 Proste generacja metody toString().
  • #33 Podstawowa adnotacja dodająca kod konstruktora bezargumentowego to NoArgsConstructor.
  • #34 Adnotacja NoArgsConstructor jest dodawana na poziomie deklaracji klasy.
  • #35 Adnotacja AllArgsConstructor generuje konstruktor z wszystkimi argumentami.
  • #36 Adnotacja AllArgsConstructor generuje konstruktor z wszystkimi argumentami.
  • #39 RequiredArgsConstructor – tworzy konstruktor z polami, które są wymagane – pola oznaczone jako final lub pola z adnotacja @NonNull. Można łączyć wiele adnotacji, więc wygenerować tylko jeden z kontstruktorów, wszystkie trzy lub dowolną ich kombinację, w zależności od naszych potrzeb. Oczywiście, można również dopisać inne, bardziej specyficzne konstruktory.
  • #40 RequiredArgsConstructor – tworzy konstruktor z polami, które są wymagane – pola oznaczone jako final lub pola z adnotacja @NonNull. Można łączyć wiele adnotacji, więc wygenerować tylko jeden z kontstruktorów, wszystkie trzy lub dowolną ich kombinację, w zależności od naszych potrzeb. Oczywiście, można również dopisać inne, bardziej specyficzne konstruktory.
  • #41 Adnotacja @Data generuje konstruktor bezargumentowy, gettery/setery wszystkich pól, metody equals, hashCode, toString.
  • #42 Połączenie wielu adnotacji w jednej klasie. Jak widać, Gettery dla wszystkich pól, Settery tylko dla dwóch pól – firstName, lastName. Do tego metody equals(), hashCode() i toString(). Konstruktory nie zostały wygenerowane, jedynie konstruktor napisany z palca. Pokazać jaka róznica w szybkości napisania klasy bez lomboka i z lombokiem.
  • #46 A method annotated with @Builder (from now on called the target) causes the following 7 things to be generated: - An inner static class named SuperheroBuilder, with the same type arguments as the static method (called the builder). In the builder: One private non-static non-final field for each parameter of the target. In the builder: A package private no-args empty constructor. In the builder: A 'setter'-like method for each parameter of the target: It has the same type as that parameter and the same name. It returns the builder itself, so that the setter calls can be chained, as in the above example. In the builder: A build() method which calls the method, passing in each field. It returns the same type that the target returns. In the builder: A sensible toString() implementation. In the class containing the target: A builder() method, which creates a new instance of the builder.
  • #47 A method annotated with @Builder (from now on called the target) causes the following 7 things to be generated: - An inner static class named SuperheroBuilder, with the same type arguments as the static method (called the builder). In the builder: One private non-static non-final field for each parameter of the target. In the builder: A package private no-args empty constructor. In the builder: A 'setter'-like method for each parameter of the target: It has the same type as that parameter and the same name. It returns the builder itself, so that the setter calls can be chained, as in the above example. In the builder: A build() method which calls the method, passing in each field. It returns the same type that the target returns. In the builder: A sensible toString() implementation. In the class containing the target: A builder() method, which creates a new instance of the builder.
  • #48 A method annotated with @Builder (from now on called the target) causes the following 7 things to be generated: - An inner static class named SuperheroBuilder, with the same type arguments as the static method (called the builder). In the builder: One private non-static non-final field for each parameter of the target. In the builder: A package private no-args empty constructor. In the builder: A 'setter'-like method for each parameter of the target: It has the same type as that parameter and the same name. It returns the builder itself, so that the setter calls can be chained, as in the above example. In the builder: A build() method which calls the method, passing in each field. It returns the same type that the target returns. In the builder: A sensible toString() implementation. In the class containing the target: A builder() method, which creates a new instance of the builder.
  • #51 Dlaczego używać projektu Lombok w projekcie? Trzy rzeczy. Mniej kodu. I to kodu, który musimy napisać i zajmuje dużo czasu, nawet jeśli używamy IDE do wygenerowania go, zajmuje to czas, który możemy spożytkować lepiej i bardziej produktywnie. Kod klasy jest czytelniejszy, co wynika z punktu pierwszego. Jak wiadomo, większość czasu poświęcamy na czytanie kodu, nie na pisanie, a zredukowanie liczby linii kodu ułatwia zapoznanie się z klasą. Szybsze programowanie. Dużo szybciej jest dodać adnotacje zamiast pisania ciała metody.
  • #52 POL 1. Niezdolność do wykrycia konstruktora klasy bazowej. To znaczy, jeżeli klasa bazowa nie posiada defaultowego konstruktora, żadna klasa dziedzicząca ni emoże użyć adnotacji @Data bez bezpośredniego napisania konstruktora, który użyje dostępnego konstruktora klasy bazowej. 2. Lombok jest ściśle powiązany z kompilatorem Javy. Ponieważ API procesora adnotacji pozwala jedynie na tworzenie nowych plików podczas kompilacji (nie modyfikację istniejących) lombok używa API jako punktu wejściowego (entry point) żeby zmodyfikować kompilator javy. Powoduje to, że użyte jest niepubliczne API. Użycie Lomboka może sprawić, że przy upgradzie kompilatora nasz kod przestanie działać. ENG 1. One important problem is the inability to detect the constructors of a superclass. This means that if a superclass has no default constructor any subclasses cannot use the @Data annotation without explicitly writing a constructor to make use of the available superclass constructor. . 2. A limitation of Lombok is the fact that it is closely tied to the java compiler. Since the annotation processor API only allows creation of new files during the compilation (and not the modification of the existing files) lombok uses that API as a entry point to modify the java compiler. Unfortunately these modifications of the compiler make heavy usage of non-public APIs. Using lombok may be a good idea but you must be aware that upgrading your compiler may broke your code.