This is the fourth day of my participation in the August More text Challenge. For details, see: August More Text Challenge
1 introduction
I recently finished reading refactoring
In the chapter on removing Settings, I discovered that I had been working with Lombok for so long that I was used to using the @data annotation for Getter/Setter configuration
So how do you remove unnecessary setters in Lombok?
Starting with this question, you can extend it to “How do YOU constrain and refactor Java classes under Lombok?”
Due to the limited ability of the author, it can only be summarized from the following aspects:
- Modifiers in Java
- Refactoring strategies in Lombok
- Possible problems with Lombok
Modifiers in Java
There are four kinds of modifiers in Java: public/default/protected/private
The special one is default, which has the same meaning as not writing any modifiers
The modifier’s accessibility range is as follows:
The modifier | Inside the class | A subclass | The same package | other |
---|---|---|---|---|
public | Square root | Square root | Square root | Square root |
default | Square root | ? | Square root | x |
protected | Square root | Square root | x | x |
private | Square root | x | x | x |
It is not clear whether the subclasses of default can be accessed, because the access condition of default is under the same package. If the subclasses are not under the same package, they cannot be accessed
Why do we introduce modifiers?
For a good program, access should be clear for every property of every class
Even with Lombok, we don’t apply the @data solve everything approach to every class that passes through our hands
Both fields/methods and getters/setters should have their own access control
If you can agree with that, the following will certainly help you
Refactoring strategies in Lombok
3.1 Self-encapsulated Fields
To Lombok’s credit, it frees up the programmer’s hands and visual sense with this refactoring strategy
- Change the properties of the class to
private
- Increments in the class header
@Data
annotations
For example, we create the Account class:
@Data
public class Account {
private String id;
}
Copy the code
You can only call the get/set method when setting the value:
public class Test {
public static void main(String[] args) {
Account account = new Account();
// The id attribute cannot be accessed directly
account.id = "test-01";
account.setId("test-01"); account.getId(); }}Copy the code
3.2 Removing the Settings function
When a field is not allowed to change after creation, we should cancel its setting function
Continuing with the kulombok, under this refactoring strategy, we only need to make the field variables final:
@Data
public class Account {
private final String id;
}
Copy the code
public class Test {
public static void main(String[] args) {
Account account = new Account("test-01");
// The setId method does not exist
account.setId("test-01"); }}Copy the code
3.3 Factory pattern replaces constructor
We should use factory functions when creating objects that do more than simply build
The Builder in Lombok provides a degree of traversal for factory functions
Suppose we create a Person class with a gender attribute where a value of 0 represents women and a value of 1 represents men:
@Data
public class Person {
public Person(int gender) {
this.gender = gender;
}
private int gender;
}
Copy the code
Now we create two special factory functions: create male and create female:
@Data
public class Person {
public Person(int gender) {
this.gender = gender;
}
private int gender;
public static Person createFemale(a) {
return new Person(0);
}
public static Person createMale(a) {
return new Person(1); }}Copy the code
Let’s use Builder to modify the Person class:
@Data
@Builder
public class Person {
private int gender;
public static Person createFemale(a) {
return Person.builder().gender(0).build();
}
public static Person createMale(a) {
return Person.builder().gender(1).build(); }}Copy the code
At first glance, it doesn’t seem to make much difference, mainly because our Person class is very simple. What if:
- The Person class has a total of 20 arguments, 12 of which need to be used in the constructor
- The Person class is a subclass of a class. The constructor needs to call the parent class, and the parent class’s constructor needs eight more parameters to pass
- In both cases, suddenly adjusting the Construction rules of the Person class at the end of development,
Then you might go crazy, and if there’s inheritance, you’ll go crazy again when you change the parent class
In factory functions, it is recommended to use @Builder and @superBuilder properly
4 possible problems with Lombok
4.1 Encapsulating A Collection
When there is a set of properties in a class, it is important to be extra vigilant. It is best to follow the following principles:
- You cannot directly manipulate a collection outside of a class
- You cannot directly assign a value to the entire property outside of the class
- The operations needed to implement the collection in the class
Taking arrays as an example, the corresponding implementation of the above three principles is:
- The getter should return an immutable copy of the array
- Remove the setter, add the initialization function, and its contents should be either traversal added or deep copy
- Add the add and remove methods
When using Lombok’s @data, the above principles are not possible and we need to adjust them
Continue with the Person class, assuming that everyone has courses to learn, and modify it with Lombok according to the encapsulating collection principle:
@Data
public class Person {
@Setter(AccessLevel.NONE)
private List<String> courses = new ArrayList<>();
public List<String> getCourses(a) {
return Collections.unmodifiableList(this.courses);
}
public void initCourses(List<String> courses) {
assert this.courses.isEmpty() : "The course array is not empty and cannot be initialized.";
this.courses.addAll(courses);
}
public void addCourse(String course) {
this.courses.add(course);
}
public void removeCourse(String course) {
this.courses.remove(course); }}Copy the code
Note: You added the assert keyword to the initCourses method. This feature is disabled by default when running Java programs and needs to be manually enabled by adding VM parameters
The actual effect is as follows:
public class Test {
public static void main(String[] args) {
// after initialization, the courses are []
Person person = new Person();
// The setCourses method does not exist
person.setCourses(Arrays.asList("test-01"."test-02"));
// ["test-01", "test-02"]
person.initCourses(Arrays.asList("test-01"."test-02"));
// The assert keyword failed to be initialized twice
person.initCourses(Arrays.asList("test-01"."test-02"));
// ["test-01", "test-02", "test-03"]
person.addCourse("test-03");
// ["test-01", "test-02"]
person.removeCourse("test-03");
// Error: the getter method has a read-only copy and cannot call add/remove, etc
person.getCourses().add("test-04"); }}Copy the code
5 concludes
I have always believed that laziness is a necessary condition for progress
But laziness here means that in terms of the amount of code/keystrokes/mental burden, learning and thinking can’t be lazy
Lombok helps us simplify our code, which is a good thing
But we should learn to be flexible about collections and the special needs of the business
There is no best technology in the world, only the most elegant way to use it