2
2
// then cut and paste the following lines to see how it works
3
3
// To exit jshell type /exit
4
4
5
+ // # Class and encapsulation
6
+
5
7
// Let's say i want to create a library of books,
6
8
// so we need a record Book and a record Library that stores the books has a list
7
9
record Book (String title , String author ) { }
@@ -24,15 +26,26 @@ System.out.println(library);
24
26
// an object has an effect to another object.
25
27
26
28
29
+ // ## Encapsulation principle
27
30
// The encapsulation principle: the only way to change the value of an object
28
31
// is to use one of the methods of this object.
29
32
30
- // To avoid the issue above, we have to make the list of books of the Library
31
- // separated from the list of books taken as parameter to initialize the Library
33
+ // In Java, the way to ensure the encapsulation principle is based on
34
+ // separating the API part (what the user code can use) from the implementation part
35
+ // (how the class is implemented).
36
+
37
+ // This separation is done by using a special syntax named __class__ that allow
38
+ // to precisely control what is part of the API and what is part of the impelemntation.
32
39
33
- // We have to use a new syntax, a class that unlike a record allows
34
- // to separate the external API (the one users see) from the internal API
35
- // (how things are implemented)
40
+ // ## Class
41
+ // A class defines
42
+ // - a field (books) that is like a record component but not necessarily visible by the user code
43
+ // - a constructor (Library), that guarantee that any objects will be correctly initialized
44
+ // - fields and methods have a visibility (public/private) so it's easy to control
45
+ // what is visible from the outside and what is not.
46
+
47
+
48
+ // ### Unmodifiable class
36
49
class Library {
37
50
private final List <Book > books ;
38
51
@@ -44,63 +57,87 @@ class Library {
44
57
return "Library " + books .toString ();
45
58
}
46
59
}
47
-
48
- // A class defines
49
- // - a field (books) that is like a record component but not necessarily visible by the user
50
- // - a constructor (Library), a special method used to initialize the library
51
- // - fields and methods have a visibility (public/private) so it's easy to control
52
- // what is visible from the outside and what is not.
53
- // Unlike a record, the method equals/hashCode() and toString() are not provided and has
54
- // to be hand written.
55
-
56
60
var library = new Library (books );
57
61
System .out .println (library );
58
62
59
- // and changing the list of books has no effect on the library
63
+ // Now changing the list of books has no effect on the library
64
+ // because the field `books` and the argument of the constructor `books` are different references
60
65
books .remove (new Book ("DaVinci Code" , "Dan Brown" ));
61
66
System .out .println (library );
62
67
63
68
// You can notice that the constructor has no return type, it's because it's always void.
69
+
64
70
// The field 'books' is declared final which means must be initialized
65
71
// in the constructor (and not changed afterward) so we are sure that in toString(),
66
72
// the field 'books' has been initialized.
67
73
74
+ // Unlike a record, the method equals/hashCode() and toString() are not provided and has
75
+ // to be hand written. We will see how to implement them later.
68
76
69
- // Writing equals()/hashCode()
70
- // If we want to be able to use Library with data structures like list or map,
71
- // We must write equals() and hashCode()
72
- // - equals() has to return false if it's not the right class, and
73
- // use the fields to see if their values are equals
74
- // - hashCode() has to use the same fields to compute a hash value
75
- class Library {
76
- private final List <Book > books ;
77
+
78
+ // ### Modifiable class
79
+ // The code above is an unmodifiable implementation of Library
80
+ // that can be used as element of list or map.
81
+ // We can also write a mutable version with the caveat that using it
82
+ // as element of a list or a map is not recommended
83
+ class ModifiableLibrary {
84
+ private final ArrayList <Book > books ;
77
85
78
- public Library (List <Book > books ) {
79
- this .books = List .copyOf (books );
86
+ public ModifiableLibrary () {
87
+ books = new ArrayList <>();
88
+ }
89
+
90
+ public void add (Book book ) {
91
+ Objects .requireNonNull (book );
92
+ books .add (book );
93
+ }
94
+
95
+ public String toString () {
96
+ return "ModifiableLibrary " + books .toString ();
80
97
}
98
+ }
99
+ var library = new ModifiableLibrary ();
100
+ library .add (new Book ("DaVinci Code" , "Dan Brown" ));
101
+ System .out .println (library );
102
+ library .add (new Book ("Effective Java" , "Joshua Bloch" ));
103
+ System .out .println (library );
104
+
105
+
106
+ // ### Modifiable class and accessor
107
+ // An error sometime seen is to add a method to get the content of the library
108
+ // and forget that it may expose the private list of books
109
+ class ModifiableLibrary {
110
+ private final ArrayList <Book > books ;
81
111
82
- public boolean equals (Object o ) {
83
- return o instanceof Library library &&
84
- books .equals (library .books );
112
+ public ModifiableLibrary () {
113
+ books = new ArrayList <>();
85
114
}
86
115
87
- public int hashCode () {
88
- return books .hashCode ();
116
+ public void add (Book book ) {
117
+ Objects .requireNonNull (book );
118
+ books .add (book );
119
+ }
120
+
121
+ public List <Book > getBooks () {
122
+ return books ;
89
123
}
90
124
91
125
public String toString () {
92
- return "Library " + books .toString ();
126
+ return "ModifiableLibrary " + books .toString ();
93
127
}
94
128
}
95
129
96
- var library1 = new Library (books );
97
- var library2 = new Library (books );
98
- System .out .println (library1 .equals (library2 ));
130
+ // The following code breaks the encapsulation because you can
131
+ // modify the library without calling a method of the Library
132
+ // (add is done on the List<Book> not on the Library)
133
+ var library = new ModifiableLibrary ();
134
+ var books = library .getBooks ();
135
+ books .add (new Book ("DaVinci Code" , "Dan Brown" ));
99
136
100
- // The code above is an unmodifiable implementation of Library
101
- // that can be used as element of list or map.
102
- // We can also write a mutable version with the caveat that using it
103
- // as element of a list or a map is not recommended
137
+ // One solution is to return a copy, or better a non modifiable view
138
+ // of the internal list of books
139
+ // The best solution being to not have a method `getBook()` at all,
140
+ // the less code you write the less bug you have.
104
141
class ModifiableLibrary {
105
142
private final ArrayList <Book > books ;
106
143
@@ -112,29 +149,29 @@ class ModifiableLibrary {
112
149
books .add (book );
113
150
}
114
151
152
+ public List <Book > getBooks () {
153
+ return Collections .unmodifiableList (books );
154
+ }
155
+
115
156
public String toString () {
116
157
return "ModifiableLibrary " + books .toString ();
117
158
}
118
159
}
119
160
var library = new ModifiableLibrary ();
120
- library .add (new Book ("DaVinci Code" , "Dan Brown" ));
121
- System .out .println (library );
122
- library .add (new Book ("Effective Java" , "Joshua Bloch" ));
123
- System .out .println (library );
161
+ var books = library .getBooks ();
162
+ books .add (new Book ("DaVinci Code" , "Dan Brown" ));
124
163
125
164
126
- // Record constructor
127
- // Records also provides a way to change the constructor used to initialize
128
- // a record called the 'canonical constructor'.
165
+ // ## Record constructor
166
+ // Records also provides ways to customize the code to respect the
167
+ // encapsulation principle
168
+ // Here, we only need to change the canonical constructor
129
169
record Library (List <Book > books ) {
130
170
public Library (List <Book > books ) {
131
171
this .books = List .copyOf (books );
132
172
}
133
173
}
134
174
135
- // In this example, it's enough to make the implementation of Library
136
- // to be compatible with the encapsulation principle
137
-
138
175
139
176
// To summarize, a class is a general mechanism to describe how things
140
177
// are implemented and make a separation between what is publicly visible
0 commit comments