-
Notifications
You must be signed in to change notification settings - Fork 67
/
Copy pathchapter07-interface.jsh
154 lines (128 loc) · 5.06 KB
/
chapter07-interface.jsh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// To starts, run jshell --enable-preview which is a program able to interpret Java syntax
// then cut and paste the following lines to see how it works
// To exit jshell type /exit
// # Interface
// Java is a typed language, even if you don't explicitly write a type
// the compiler you compute the type of every variables
// Once you start to want to mix several records, you may need to declare
// common type between records, such type are known as interface
// ## The problem
// let say we have a Square and Rectangle, and both have a method `area()`
record Square(int side) {
public double area() {
return side * side;
}
}
record Rectangle(int width, int height) {
public double area() {
return width * height;
}
}
// let create a list of a square and a rectangle
var figures = List.of(new Square(2), new Rectangle(3, 4));
// try to loop over the elements of the figures to print the area doesn't compile
for(var figure: figures) {
System.out.println(figure.area());
}
// The problem is that compiler try to find the type of the element of the list
// and find that they are java.lang.Object, and Object has no method area()
// so the code does not compile
// ### Interface and abstract method
// The idea is to introduce a type Figure has a common type for Square and Rectangle.
// In Java, we use the keyword `interface` for that.
// The method `area()` in Figure is not a classical method with some code because
// the code is defined in Square and Rectangle. It's an `abstract` method.
// The definition of the method is present but the code has to be implemented by the
// records that implement the interface
interface Figure {
public abstract double area();
}
// and declare that a Square and a Rectangle are a kind of Figure
// using the keyword `implements`
record Square(int side) implements Figure {
public double area() {
return side * side;
}
}
record Rectangle(int width, int height) implements Figure {
public double area() {
return width * height;
}
}
// Now, the list is correctly typed as a list of figure (`List<Figure>`)
// so looping over the figures to call `area()` works
List<Figure> figures = List.of(new Square(2), new Rectangle(3, 4));
for(var figure: figures) {
System.out.println(figure.area());
}
// An interface is a common type that you need to declare when you want to
// call the same method on different records
// At runtime, when you call a method of the interface, the virtual machine calls
// the correct implementation (this is called polymorphism)
// ## Static method
// Like a record, an interface can have `static` methods
interface Figure {
public abstract double area();
public static Figure createASquare(int side) {
return new Square(side);
}
}
var aSquare = Figure.createASquare(3);
System.out.println(aSquare);
// ## Default method
// Inside an interface, the instance methods are implicitly abstract,
// if we want to declare a method with some code in it, we have to use
// the keyword `default`.
// By example, we can write a method `isBig` that is true if the area is big enough.
interface Figure {
public abstract double area();
public default boolean isBig() {
return area() >= 10;
}
}
System.out.println(new Square(2).isBig());
System.out.println(new Rectangle(3, 4).isBig());
// Because a default method is declared on the interface, all records that
// implement that interface will have that method. Default methods are named like this
// because if a record that implements the interface doesn't define the method,
// the method will be provided by default.
// ## Functional interface
// An interface with only one abstract method is equivalent to a function type.
// We name this kind of interfaces, _functional_ interfaces.
// They can be implemented by two special constructs.
// ### Lambda
// The parameter are declared in between the parenthesis and the body of the method
// is declared after the arrow (like the expression switch).
interface Figure {
public abstract double area();
}
Figure anotherFigure = () -> 4;
System.out.println(anotherFigure.area());
// and rewrite the method rectangularTriangle()
// You can notice that a lambda can access to the parameter `width` and `height`
Figure rectangularTriangle(int width, int height) {
return () -> width * height / 2.0;
}
var triangle = rectangularTriangle(3, 4);
System.out.println(triangle.area());
// ### Method Reference
// In case of the method already exists instead of
// calling it inside a lambda, we can make a reference on it using the operator ::
// (notice that EquilaterlaTriangle doesn't implement Figure)
record EquilateralTriangle(int side) {
double area() {
return Math.sqrt(3) * side * side / 4.0;
}
}
var equilateral = new EquilateralTriangle(2);
// so instead of
var figures = List.<Figure>of(new Square(2), () -> equilateral.area());
for(var figure: figures) {
System.out.println(figure.area());
}
// you can use a method reference
var figures = List.<Figure>of(new Square(2), equilateral::area);
for(var figure: figures) {
System.out.println(figure.area());
}
// More about lambdas and method references in the following chapter.