You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We all understand that we should code to interfaces. Interfaces give client a contract which they should use without relying on the implementation details(i.e. classes). Hence, promoting **loose coupling**. Designing clean interfaces is one of the most important aspect of API design. One of the SOLID principle **[Interface segregation](https://en.wikipedia.org/wiki/Interface_segregation_principle)** talks about designing smaller client-specific interfaces instead of designing one general purpose interface. Interface design is the key to clean and effective API's for your libraries and applications.
4
+
We all understand that we should code to interfaces. Interfaces give client a contract which they should use without relying on the implementation details(i.e. classes). Hence, promoting **[loose coupling](https://en.wikipedia.org/wiki/Loose_coupling)**. Designing clean interfaces is one of the most important aspect of API design. One of the SOLID principle **[Interface segregation](https://en.wikipedia.org/wiki/Interface_segregation_principle)** talks about designing smaller client-specific interfaces instead of designing one general purpose interface. Interface design is the key to clean and effective API's for your libraries and applications.
4
5
5
-
You can follow the **7 Days with Java 8** series at http://shekhargulati.com/7-days-with-java-8/
6
+
If you have designed any API then with time you would have felt the need to add new methods to the API. Once API is published it becomes impossible to add methods to an interface without breaking existing implementations. To make this point clear, let's suppose you are building a simple `Calculator` API that supports `add`,`subtract`, `divide`, and `multiply` operations. We can write `Calculator` interface as shown below. ***To keep things simple we will use int.***
6
7
7
-
If you have designed any API then with time you would have felt the need to add new methods to the API. Once API is published it becomes impossible to add methods to an interface without breaking existing implementations. To make this point clear, let's suppose you are building a client API for a social network and one of the API's that you want to design is to offer user suggestions based on user initials. So, if someone calls your API method with initial `sh` then you would suggest users like `Shekhar`, `Shankar`, `Shane`. So, you came up with `UserSuggester` interface as shown below.
8
+
```java
9
+
publicinterfaceCalculator {
10
+
11
+
intadd(intfirst, intsecond);
12
+
13
+
intsubtract(intfirst, intsecond);
14
+
15
+
intdivide(intnumber, intdivisor);
16
+
17
+
intmultiply(intfirst, intsecond);
18
+
}
19
+
```
20
+
21
+
To back this `Calculator` interface you created a `BasicCalculator` implementation as shown below.
8
22
9
23
```java
10
-
publicinterfaceUserSuggester {
11
-
List<User>suggestions(Stringinitials);
24
+
publicclassBasicCalculatorimplementsCalculator {
25
+
26
+
@Override
27
+
publicintadd(intfirst, intsecond) {
28
+
return first + second;
29
+
}
30
+
31
+
@Override
32
+
publicintsubtract(intfirst, intsecond) {
33
+
return first - second;
34
+
}
35
+
36
+
@Override
37
+
publicintdivide(intnumber, intdivisor) {
38
+
if (divisor ==0) {
39
+
thrownewIllegalArgumentException("divisor can't be zero.");
40
+
}
41
+
return number / divisor;
42
+
}
43
+
44
+
@Override
45
+
publicintmultiply(intfirst, intsecond) {
46
+
return first * second;
47
+
}
12
48
}
13
49
```
14
50
15
-
API turned out to be very useful to the API consumers and some of the consumers decided to have their own implementation of the `UserSuggester` interface. After talking to your users you came to know that most of them would like to have a way to specify filter criteria as well so that they can filter down user suggestions. It looked a very simple API change so you added one more method to the API -- `suggestions(String initials, Predicate<User> predicate)`.
51
+
## Static Factory Methods
52
+
53
+
Calculator API turned out to be very useful and easy to use. Users just have to create an instance of `BasicCalculator` and then they can use the API. You start seeing code like the one shown below.
54
+
55
+
```java
56
+
Calculator calculator =newBasicCalculator();
57
+
int sum = calculator.add(1, 2);
58
+
59
+
BasicCalculator cal =newBasicCalculator();
60
+
int difference = cal.subtract(3, 2);
61
+
```
62
+
63
+
Oh no!! Users of the API are not coding to `Calculator` interface instead they are coding to implementation. Your API didn't enforced users to code to interfaces as the `BasicCalculator` class was public. If you make `BasicCalculator` package protected then you would have to provide a static factory class that will take care of providing the `Calculator` implementation. Let's improve the code to handle this.
16
64
65
+
First, we will make `BasicCalculator` package protected so that users can't access the class directly.
Adding a method to an interface broke the source compatibility of the API. Adding methods to an interface leads to compilation failures when classes that depend on the interface don't implement the method. This means users who were implementing `UserSuggester` interface would have to add implementation for `suggestions(String initials, Predicate<User> predicate)` otherwise their code will not compile. This is a big problem for API designers as it makes difficult to evolve API. Prior to Java 8, it was not possible to have method implementations inside interfaces. This often becomes a problem when it was required to extend an API i.e. adding one or more methods to the interface definition.
72
+
Next, we will write a factory class that will give us the `Calculator` instance as shown below.
25
73
26
-
Java 8 introduced two ways to declare method with implementations inside an interface. These are:
74
+
```java
75
+
publicabstractclassCalculatorFactory {
27
76
28
-
***static methods**: This allows users to declare static methods inside an interface. `Stream` interface has few static helper methods like `of`, `empty`, `concat`, etc. The static methods inside an interface could be used to replace static helper classes that we normally create to define helper methods associated with a type. For example, `Collections` class is a helper class that defines various helper methods to work with Collection and associated interfaces. The methods define in `Collections` class could easily be added to `Collection` or its child interface. For example, `addAll` method in the `Collections` class can be added to `Collection` interface as a static method.
77
+
publicstaticCalculatorgetInstance() {
78
+
returnnewBasicCalculator();
79
+
}
80
+
}
81
+
```
82
+
83
+
Now, users will be forced to code to `Calculator` interface and they will not have access to implementations.
84
+
85
+
Although we have achieved our goal but we have increased the circumference area of our API by adding a new class `CalculatorFactory`. Now users of the API have to learn about one more class before they can use the API effectively. This was the only solution available before Java 8.
86
+
87
+
**Java 8 allows you to declare static methods inside an interface**. This will allow API designers to define static utility methods like `getInstance` in the interface itself. Hence keeping API short and lean. The static methods inside an interface could be used to replace static helper classes(`CalculatorFactory`) that we normally create to define helper methods associated with a type. For example, `Collections` class is a helper class that defines various helper methods to work with Collection and associated interfaces. The methods define in `Collections` class could easily be added to `Collection` or its child interface.
88
+
89
+
The above code can be improved in Java 8 by adding a static `getInstance` method in the `Calculator` interface itself.
29
90
30
91
```java
31
-
publicstatic<T>Stream<T> of(T... values) {
32
-
returnArrays.stream(values);
92
+
publicinterfaceCalculator {
93
+
94
+
staticCalculatorgetInstance() {
95
+
returnnewBasicCalculator();
96
+
}
97
+
98
+
intadd(intfirst, intsecond);
99
+
100
+
intsubtract(intfirst, intsecond);
101
+
102
+
intdivide(intnumber, intdivisor);
103
+
104
+
intmultiply(intfirst, intsecond);
105
+
33
106
}
34
107
```
35
108
36
-
***default methods**: This allows users to provide default implementations to methods defined in the interface. The class implementing the interface is not required to provide implementation of the method. If implementing class provides the implementation then that implementation would be used else default implementation is used. `List` interface has few default methods defined in it like `replaceAll`, `sort`, and `splitIterator`.
109
+
## Evolving API with time
110
+
111
+
Some of the consumers decided to either extend the `Calculator` API by adding methods like `remainder` or write their own implementation of `Calculator` interface. After talking to your users you came to know that most of them would like to have a `remainder` method added to `Calculator` interface. It looked a very simple API change so you added one more method to the API.
112
+
113
+
```java
114
+
publicinterfaceCalculator {
115
+
116
+
staticCalculatorgetInstance() {
117
+
returnnewBasicCalculator();
118
+
}
119
+
120
+
intadd(intfirst, intsecond);
121
+
122
+
intsubtract(intfirst, intsecond);
123
+
124
+
intdivide(intnumber, intdivisor);
125
+
126
+
intmultiply(intfirst, intsecond);
127
+
128
+
intremainder(intnumber, intdivisor); // new method added to API
129
+
}
130
+
```
131
+
132
+
Adding a method to an interface broke the source compatibility of the API. This means users who were implementing `Calculator` interface would have to add implementation for `remainder` method otherwise their code will not compile. This is a big problem for API designers as it makes difficult to evolve API. Prior to Java 8, it was not possible to have method implementations inside interfaces. This often becomes a problem when it was required to extend an API i.e. adding one or more methods to the interface definition.
133
+
134
+
To allow API's to evolve with time, Java 8 allows users to provide default implementations to methods defined in the interface. These are called **default** or **defender** methods. The class implementing the interface is not required to provide implementation of these methods. If implementing class provides the implementation then implementing class method implementation will be used else default implementation will be used. `List` interface has few default methods defined like `replaceAll`, `sort`, and `splitIterator`.
We can solve our API problem by defining a default method as shown below.
146
+
We can solve our API problem by defining a default method as shown below. Default methods are usually defined using already existing methods -- `remainder` is defined using `subtract`, `multiply`, and `divide` methods.
A class can extend a single class but can implement multiple interfaces. Now that it is feasible to have method implementations in interfaces Java has multiple inheritance of behavior. Java already has multiple inheritance at type level but now it also has multiple inheritance at behavioral level. There are three resolution rules that helps decide which method will be picked:
156
+
A class can extend a single class but can implement multiple interfaces. Now that it is feasible to have method implementations in interfaces Java has multiple inheritance of behavior. Java already has multiple inheritance at type level but now it also has multiple inheritance at behavior level. There are three resolution rules that helps decide which method will be picked:
61
157
62
158
1. Methods declared in classes win over method defined in interfaces.
0 commit comments