-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFunctionalInterfaceExample.java
More file actions
270 lines (187 loc) Β· 8.18 KB
/
FunctionalInterfaceExample.java
File metadata and controls
270 lines (187 loc) Β· 8.18 KB
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
package DataStructures;
import java.util.function.Function;
import java.util.function.Predicate;
/**
Functional interfaces in Java is introduced in v8
An Interface that contains exactly one abstract method is known as functional interface. ---> @FunctionalInterface annotation is optional
It can have any number of default, static methods but can contain only one abstract method.
It can also declare methods of object class.
Functional Interface is also known as Single Abstract Method Interfaces or SAM Interfaces.
USE CASES
we can define a method with functional interface as a parameter
Examples:
Collections.sort(nums, ()->), reduce(), map(), filter()
List.forEach()
HashMap.merge(), HashMap.compute()
3 Ways to implement functional interface
1) Anonymous inner class (new Interface() {}) Java 1+ --> creates a new inner class and allocates memory in heap -- not recommended
2) Lambda expression
3) Method reference
NOTE:
1) All java lambda methods and method references use this functional interface principle
2) We can override multiple methods in Anonymous inner class but only one method in Lambda expression and method reference
3) We can also create inner interfaces just like inner classes
4) If built-in Functional interfaces abstract methods don't have throws Exception, then we can't throw Exception in lambda expression and method reference
Function<String, String> func = (input) -> {
throw new IOException("fail"); // β Compiler error: unhandled exception
};
So, wrap it in try-catch block
Function<String, String> func = (input) -> {
try {
throw new Exception("fail"); // β
works
} catch (Exception e) {
throw new RuntimeException(e);
}
};
* @author Srinvas Vadige, srinivas.vadige@gmail.com
* @since 21 Sept 2024
*/
public class FunctionalInterfaceExample {
// 4 ways to define and use single method interface
@SuppressWarnings("unused")
public static void main(String[] args) {
/*
1) Anonymous inner class (new Interface() {}) -------------------
defining the single method interface declaration using anonymous class,
implement the interface in our class and override the method
initialize the interface with "new keyword" and @override the method
*/
SampleInterface i_instance = new SampleInterface() {
@Override
public String display() {
return "Instance for interface";
}
};
System.out.println(i_instance.display());
/*
2) Lambda expression -------------------
defining the single method interface with lambda
Lambda expressions cannot throw checked exceptions unless declared
*/
SampleInterface i_lambda = () -> "lambda"; // or ()->{return "lambda"}
System.out.println(i_lambda.display());
/*
3) Method reference -------------------
use our class method as interface method reference
π FunctionalInterfaceExample::show ---> if show method is static, we can implement this statement in anywhere -- static class variable, non-static class variable, static method, non-static method
π new FunctionalInterfaceExample()::showNonStatic ---> if showNonStatic method is non-static and current statement in static method like psvm main()
π this::showNonStatic ---> if showNonStatic method is non-static and current statement in non-static method
*/
SampleInterface i_methodReference = FunctionalInterfaceExample::show;
i_methodReference.display(); // if we have parameters, then pass here not in show method
SampleInterface i_methodReferenceNonStatic = new FunctionalInterfaceExample()::showNonStatic; // or this::showNonStatic π₯
i_methodReferenceNonStatic.display();
// Function Functional Interface examples
Function<Integer, Integer> f1 = (i) -> i * i;
Function<Object, String> f2 = Object::toString;
// Function<String, String> func = (input) -> {
// throw new Exception("fail"); // β Compiler error: unhandled exception
// };
Function<String, String> func = (input) -> {
try {
throw new Exception("fail"); // β
works
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
// static class method
static String show() {
return "show using static method";
}
// non-static class method
public String showNonStatic() {
return "show using non-static method";
}
@FunctionalInterface // optional
interface SampleInterface {
public String display();
}
/*
A functional interface can contain implementations in default methods
but should have only one un-implemented method
*/
interface MyFunctionalInterface {
public void execute();
public default void print(String text) {
System.out.println(text);
}
}
/*
A functional interface can extend another interface only when it does not have any abstract method.
*/
interface Sayable{
void say(String msg); // abstract method
}
// @FunctionalInterface // ===> cannot declare it as @FunctionalInterface
// cause Invalid '@FunctionalInterface' annotation; Doable is not a functional interface
interface Doable extends Sayable{
void doIt();
}
interface Sayable2 {
default void doIt(){ // non-abstract method
System.out.println("Do it now");
}
}
@FunctionalInterface
interface Doable2 extends Sayable2{
void say(String msg); // abstract method
}
/*
Built-in Functional Interfaces in Java
--------------------------------------
Since Java SE 1.8 onwards, there've been many interfaces that are converted into functional interfaces. All these interfaces are annotated with @FunctionalInterface. These interfaces are as follows β
1) Runnable β> This interface only contains the run() method.
2) Comparable β> This interface only contains the compareTo() method.
3) ActionListener β> This interface only contains the actionPerformed() method.
4) Callable β> This interface only contains the call() method.
Java SE 8 included four main kinds of functional interfaces which can be applied in multiple situations as mentioned below
1) Function (java.util.function.Function)
2) Predicate (java.util.function.Predicate)
3) UnaryOperator
4) BinaryOperator
5) Supplier
6) Consumer
6) BiConsumer<T,U>
7) BiFunction<T,U,R>
8) DoubleFunction<R>
9) ToLongFunction<T> ..........
*/
/*
Java Functional Composition
---------------------------
Functional composition is a technique to combine multiple functions into a
single function which uses the combined functions internally.
You can compose individual functions (typically one or more Java Lambda Expressions) into a single function yourself,
but Java also comes with built-in support for functional composition to make the job easier for you.
For example, we can see "how to compose functions from smaller functions via Java's built-in features"
https://jenkov.com/tutorials/java-functional-programming/functional-composition.html
*/
static class FunctionalCompositionExample {
static void sampleFunctionalComp() {
Predicate<String> startsWithA = (text) -> text.startsWith("A");
Predicate<String> endsWithX = (text) -> text.endsWith("x");
Predicate<String> startsWithAAndEndsWithX = (text) -> startsWithA.test(text) && endsWithX.test(text);
String input = "A hardworking person must relax";
boolean result = startsWithAAndEndsWithX.test(input);
System.out.println(result);
Predicate<String> composedAnd = startsWithA.and(endsWithX);
String input2 = "A hardworking person must relax";
boolean result2 = composedAnd.test(input2);
System.out.println(result2);
Predicate<String> composedOr = startsWithA.or(endsWithX);
String input3 = "A hardworking person must relax sometimes";
boolean result3 = composedOr.test(input3);
System.out.println(result3);
Function<Integer, Integer> multiply = (value) -> value * 2;
Function<Integer, Integer> add = (value) -> value + 3;
multiply.apply(3);
Function<Integer, Integer> addThenMultiply = multiply.compose(add);
Integer resultFunc = addThenMultiply.apply(3);
System.out.println(resultFunc);
Function<Integer, Integer> multiplyThenAdd = multiply.andThen(add);
Integer resultFunc2 = multiplyThenAdd.apply(3);
System.out.println(resultFunc2);
}
}
}