Skip to content

Commit 8ee6913

Browse files
committed
add wrapper types and change interface a bit
1 parent 135e90f commit 8ee6913

23 files changed

+1423
-193
lines changed

README.md

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,29 @@ A guide to modern Java (Java 17)
77
1. [basictypes.md](guide/chapter01-basictypes.md)
88
2. [methods.md](guide/chapter02-methods.md)
99
3. [jshellvsjava.md](guide/chapter03-jshellvsjava.md)
10-
4. [controlflow.md](guide/chapter04-controlflow.md)
11-
5. [numbers.md](guide/chapter06-numbers.md)
10+
4. [numbers.md](guide/chapter04-numbers.md)
11+
5. [controlflow.md](guide/chapter05-controlflow.md)
1212
6. [interface.md](guide/chapter07-interface.md)
1313
7. [lambda.md](guide/chapter08-lambda.md)
1414
8. [listandmap.md](guide/chapter09-listandmap.md)
1515
9. [stringformatting.md](guide/chapter10-stringformatting.md)
16-
10. [exception.md](guide/chapter11-exception.md)
17-
11. [encapsulation.md](guide/chapter12-encapsulation.md)
16+
10. [encapsulation.md](guide/chapter11-encapsulation.md)
17+
11. [equals_hashCode_toString.md](guide/chapter12-equals_hashCode_toString.md)
1818
12. [contract.md](guide/chapter13-contract.md)
19-
13. [nullandoptional.md](guide/chapter14-nullandoptional.md)
20-
14. [inheritance.md](guide/chapter15-inheritance.md)
21-
15. [internalclass.md](guide/chapter17-internalclass.md)
22-
16. [generics.md](guide/chapter20-generics.md)
23-
17. [wrapper.md](guide/chapter21-wrapper.md)
24-
18. [variance.md](guide/chapter22-variance.md)
25-
19. [stream.md](guide/chapter25-stream.md)
26-
20. [collector.md](guide/chapter26-collector.md)
27-
21. [datastructure.md](guide/chapter30-datastructure.md)
28-
22. [sort.md](guide/chapter31-sort.md)
19+
13. [modifable_vs_mutalble.md](guide/chapter13-modifable_vs_mutalble.md)
20+
14. [nullandoptional.md](guide/chapter14-nullandoptional.md)
21+
15. [inheritance.md](guide/chapter15-inheritance.md)
22+
16. [exception.md](guide/chapter16-exception.md)
23+
17. [enum.md](guide/chapter17-enum.md)
24+
18. [internalclass.md](guide/chapter18-internalclass.md)
25+
19. [implementing_interface.md](guide/chapter19-implementing_interface.md)
26+
20. [generics.md](guide/chapter20-generics.md)
27+
21. [wrapper.md](guide/chapter21-wrapper.md)
28+
22. [variance.md](guide/chapter22-variance.md)
29+
23. [stream.md](guide/chapter25-stream.md)
30+
24. [collector.md](guide/chapter26-collector.md)
31+
25. [datastructure.md](guide/chapter30-datastructure.md)
32+
26. [sort.md](guide/chapter31-sort.md)
2933

3034

3135
## Using Java Shell (jshell)
File renamed without changes.
File renamed without changes.

chapter07-interface.jsh

Lines changed: 51 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// Once you start to want to mix several records, you need to declare
99
// common type between records, such type are known as interface
1010

11-
// ### The problem
11+
// ## The problem
1212
// let say we have a Square and Rectangle, and both have a method `area()`
1313
record Square(int side) {
1414
public double area() {
@@ -33,13 +33,21 @@ var figures = List.of(new Square(2), new Rectangle(3, 4));
3333
// and find that they are java.lang.Object, and Object has no method area()
3434
// so it doens't compile
3535

36-
// the idea is to introduce a type Figure has a common type for Square and Rectangle
36+
37+
// ### Interface and abstract method
38+
// The idea is to introduce a type Figure has a common type for Square and Rectangle.
39+
// In Java, we use the keyword `interface` for that.
40+
41+
// The method `area()` in Figure is not a classical method with some code because
42+
// the code is defined in Square and Rectangle. It's an `abstract` method.
43+
// The definition of the method is present but the code has to be implemented by the
44+
// records that implement the interface
3745
interface Figure {
38-
public double area();
46+
public abstract double area();
3947
}
4048

4149
// and declare that a Square and a Rectangle are a kind of Figure
42-
// using the keyword 'implements'
50+
// using the keyword `implements`
4351
record Square(int side) implements Figure {
4452
public double area() {
4553
return side * side;
@@ -51,9 +59,9 @@ record Rectangle(int width, int height) implements Figure {
5159
}
5260
}
5361

54-
// Now, the list is correctly typed as a list of figure (List<Figure>)
55-
// so looping over the figures to call area() works
56-
var figures = List.of(new Square(2), new Rectangle(3, 4));
62+
// Now, the list is correctly typed as a list of figure (`List<Figure>`)
63+
// so looping over the figures to call `area()` works
64+
List<Figure> figures = List.of(new Square(2), new Rectangle(3, 4));
5765
for(var figure: figures) {
5866
System.out.println(figure.area());
5967
}
@@ -63,50 +71,53 @@ for(var figure: figures) {
6371
// At runtime, when you call a method of the interface, the interpreter calls
6472
// the correct implementation (this is called polymorphism)
6573

66-
// Technically, we have already used interfaces, List is an interface too
6774

75+
// ## Static method
76+
// Like a record, an interface can have `static` methods
77+
interface Figure {
78+
public abstract double area();
79+
80+
public static Figure createASquare(int side) {
81+
return new Square(side);
82+
}
83+
}
84+
var aSquare = Figure.createASquare(3);
85+
System.out.println(aSquare);
6886

69-
// ## Implementing an interface
70-
// In Java, not only record can implement an interface,
71-
// you have three other syntax
72-
// - anonymous class
73-
// - lambda
74-
// - method reference
7587

76-
// ### Anonymous class
77-
var anotherFigure = new Figure() {
78-
public double area() {
79-
return 4;
88+
// ## Default method
89+
// Inside an interface, the instance methods are implicitly abstract,
90+
// if we want to declare a method with some code in it, we have to use
91+
// the keyword `default`.
92+
// By example, we can write a method `isBig` that is true if the area is big enough.
93+
interface Figure {
94+
public abstract double area();
95+
96+
public default boolean isBig() {
97+
return area() >= 10;
8098
}
81-
};
82-
83-
// An anonymous class allow you to only provide the code of the methods of the interface
84-
// note that the syntax is a little weird because you may call new on a Figure but infact,
85-
// you ask to create something that implements Figure not a figure by itself.
99+
}
100+
System.out.println(new Square(2).isBig());
101+
System.out.println(new Rectangle(3, 4).isBig());
86102

87-
// you may think that this syntax is useless because you can not have the area computed
88-
// from the values of some components like with a record, but if you create an anonymous class
89-
// inside a method you can use the parameters of the method inside the anonymous class
90-
Figure rectangularTriangle(int width, int height) {
91-
return new Figure() {
92-
public double area() {
93-
return width * height / 2.0;
94-
}
95-
};
96-
};
103+
// Because a default method is declared on the interface, all records that
104+
// implement that interface will have that method. Default methods are named like this
105+
// because if the record doesn't define the method itself, the method will be provided
106+
// by default.
97107

98-
var figures = List.of(new Square(2), rectangularTriangle(3, 4));
99-
for(var figure: figures) {
100-
System.out.println(figure.area());
101-
}
102108

109+
// ## Functional interface
110+
// An interface with only one abstract method is equivalent to a function type.
111+
// We name this kind of interfaces, _functional_ interfaces.
112+
// They can be implemented by supplementary constructs.
103113

104114
// ### Lambda
105-
// In case of the interface is itself an interface with only one abstract method,
106-
// we calls that interface a functional interface, you have even a shorter syntax
115+
// The parameter are declared in between the parenthesis and the body of the method
116+
// is declared after the arrow (like the expression switch).
107117
Figure anotherFigure = () -> 4;
108118

109-
// and rewrite the method rectangularTriangle() like this
119+
// and rewrite the method rectangularTriangle()
120+
// You can notice that a lambda can access to the parameter `width` and `height`
110121
Figure rectangularTriangle(int width, int height) {
111122
return () -> width * height / 2.0;
112123
}

chapter12-encapsulation.jsh renamed to chapter11-encapsulation.jsh

Lines changed: 85 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// then cut and paste the following lines to see how it works
33
// To exit jshell type /exit
44

5+
// # Class and encapsulation
6+
57
// Let's say i want to create a library of books,
68
// so we need a record Book and a record Library that stores the books has a list
79
record Book(String title, String author) { }
@@ -24,15 +26,26 @@ System.out.println(library);
2426
// an object has an effect to another object.
2527

2628

29+
// ## Encapsulation principle
2730
// The encapsulation principle: the only way to change the value of an object
2831
// is to use one of the methods of this object.
2932

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.
3239

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
3649
class Library {
3750
private final List<Book> books;
3851

@@ -44,63 +57,87 @@ class Library {
4457
return "Library " + books.toString();
4558
}
4659
}
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-
5660
var library = new Library(books);
5761
System.out.println(library);
5862

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
6065
books.remove(new Book("DaVinci Code", "Dan Brown"));
6166
System.out.println(library);
6267

6368
// You can notice that the constructor has no return type, it's because it's always void.
69+
6470
// The field 'books' is declared final which means must be initialized
6571
// in the constructor (and not changed afterward) so we are sure that in toString(),
6672
// the field 'books' has been initialized.
6773

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.
6876

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;
7785

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();
8097
}
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;
81111

82-
public boolean equals(Object o) {
83-
return o instanceof Library library &&
84-
books.equals(library.books);
112+
public ModifiableLibrary() {
113+
books = new ArrayList<>();
85114
}
86115

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;
89123
}
90124

91125
public String toString() {
92-
return "Library " + books.toString();
126+
return "ModifiableLibrary " + books.toString();
93127
}
94128
}
95129

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"));
99136

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.
104141
class ModifiableLibrary {
105142
private final ArrayList<Book> books;
106143

@@ -112,29 +149,29 @@ class ModifiableLibrary {
112149
books.add(book);
113150
}
114151

152+
public List<Book> getBooks() {
153+
return Collections.unmodifiableList(books);
154+
}
155+
115156
public String toString() {
116157
return "ModifiableLibrary " + books.toString();
117158
}
118159
}
119160
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"));
124163

125164

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
129169
record Library(List<Book> books) {
130170
public Library(List<Book> books) {
131171
this.books = List.copyOf(books);
132172
}
133173
}
134174

135-
// In this example, it's enough to make the implementation of Library
136-
// to be compatible with the encapsulation principle
137-
138175

139176
// To summarize, a class is a general mechanism to describe how things
140177
// are implemented and make a separation between what is publicly visible

0 commit comments

Comments
 (0)