0
# Functional Interfaces
1
2
Extended function interfaces with composition, memoization, and exception handling capabilities that enhance Java's standard functional interfaces.
3
4
## Capabilities
5
6
### Function Interfaces
7
8
Extended function interfaces from arity 0 to 8 with additional methods for composition, memoization, and currying.
9
10
```java { .api }
11
/**
12
* Supplier function (0 arguments)
13
*/
14
@FunctionalInterface
15
interface Function0<R> extends Supplier<R> {
16
// Factory methods
17
static <R> Function0<R> of(Function0<R> function);
18
static <R> Function0<R> constant(R value); // Function that always returns value
19
20
// Execution
21
R apply(); // Execute the function
22
23
// Memoization
24
Function0<R> memoized(); // Cache result after first call
25
boolean isMemoized(); // Check if this function is memoized
26
27
// Composition
28
default <V> Function0<V> andThen(Function<? super R, ? extends V> after);
29
30
// Lifting
31
static <R> Function1<Integer, R> lift(Function0<R> function); // Lift to Function1 ignoring argument
32
}
33
34
/**
35
* Single argument function
36
*/
37
@FunctionalInterface
38
interface Function1<T1, R> extends Function<T1, R> {
39
// Factory methods
40
static <T1, R> Function1<T1, R> of(Function1<T1, R> function);
41
static <T1, R> Function1<T1, R> identity(); // Function returning its argument
42
static <T1, R> Function1<T1, R> constant(R value); // Function ignoring argument, returning value
43
44
// Execution
45
R apply(T1 t1); // Apply function to argument
46
47
// Memoization
48
Function1<T1, R> memoized(); // Cache results by argument
49
boolean isMemoized();
50
51
// Composition
52
default <V> Function1<T1, V> andThen(Function<? super R, ? extends V> after);
53
default <V> Function1<V, R> compose(Function<? super V, ? extends T1> before);
54
55
// Currying (for compatibility - Function1 is already curried)
56
Function1<T1, R> curried();
57
58
// Partial application
59
Function0<R> apply(T1 t1); // Partially apply argument, return Function0
60
61
// Lifting operations
62
static <T1, R> Function1<Option<T1>, Option<R>> lift(Function1<T1, R> function);
63
static <T1, R> Function1<Try<T1>, Try<R>> liftTry(Function1<T1, R> function);
64
}
65
66
/**
67
* Two argument function (BiFunction)
68
*/
69
@FunctionalInterface
70
interface Function2<T1, T2, R> extends BiFunction<T1, T2, R> {
71
// Factory methods
72
static <T1, T2, R> Function2<T1, T2, R> of(Function2<T1, T2, R> function);
73
static <T1, T2, R> Function2<T1, T2, R> constant(R value);
74
75
// Execution
76
R apply(T1 t1, T2 t2); // Apply function to arguments
77
78
// Memoization
79
Function2<T1, T2, R> memoized(); // Cache results by argument tuple
80
boolean isMemoized();
81
82
// Composition
83
default <V> Function2<T1, T2, V> andThen(Function<? super R, ? extends V> after);
84
85
// Currying
86
Function1<T1, Function1<T2, R>> curried(); // Convert to curried form
87
88
// Partial application
89
Function1<T2, R> apply(T1 t1); // Apply first argument
90
Function0<R> apply(T1 t1, T2 t2); // Apply both arguments
91
92
// Argument manipulation
93
Function2<T2, T1, R> reversed(); // Swap argument order
94
95
// Lifting operations
96
static <T1, T2, R> Function2<Option<T1>, Option<T2>, Option<R>> lift(Function2<T1, T2, R> function);
97
}
98
99
/**
100
* Three argument function
101
*/
102
@FunctionalInterface
103
interface Function3<T1, T2, T3, R> {
104
// Factory methods
105
static <T1, T2, T3, R> Function3<T1, T2, T3, R> of(Function3<T1, T2, T3, R> function);
106
static <T1, T2, T3, R> Function3<T1, T2, T3, R> constant(R value);
107
108
// Execution
109
R apply(T1 t1, T2 t2, T3 t3); // Apply function to arguments
110
111
// Memoization
112
Function3<T1, T2, T3, R> memoized();
113
boolean isMemoized();
114
115
// Composition
116
default <V> Function3<T1, T2, T3, V> andThen(Function<? super R, ? extends V> after);
117
118
// Currying
119
Function1<T1, Function1<T2, Function1<T3, R>>> curried(); // Convert to curried form
120
121
// Partial application
122
Function2<T2, T3, R> apply(T1 t1); // Apply first argument
123
Function1<T3, R> apply(T1 t1, T2 t2); // Apply first two arguments
124
Function0<R> apply(T1 t1, T2 t2, T3 t3); // Apply all arguments
125
}
126
127
// ... continues similarly for Function4 through Function8 with appropriate arities
128
```
129
130
**Usage Examples:**
131
132
```java
133
import io.vavr.Function1;
134
import io.vavr.Function2;
135
import io.vavr.Function3;
136
137
// Creating functions
138
Function1<String, Integer> length = String::length;
139
Function2<Integer, Integer, Integer> add = Integer::sum;
140
Function3<Integer, Integer, Integer, Integer> add3 = (a, b, c) -> a + b + c;
141
142
// Function composition
143
Function1<String, String> toUpper = String::toUpperCase;
144
Function1<String, Integer> upperLength = length.compose(toUpper);
145
Function1<String, String> describe = length.andThen(len -> "Length: " + len);
146
147
// Memoization (caching results)
148
Function1<Integer, Integer> expensive = n -> {
149
System.out.println("Computing for " + n);
150
return n * n;
151
};
152
Function1<Integer, Integer> cached = expensive.memoized();
153
154
cached.apply(5); // Prints "Computing for 5", returns 25
155
cached.apply(5); // Returns 25 without printing (cached)
156
157
// Currying
158
Function2<Integer, Integer, Integer> multiply = (a, b) -> a * b;
159
Function1<Integer, Function1<Integer, Integer>> curried = multiply.curried();
160
Function1<Integer, Integer> multiplyBy5 = curried.apply(5);
161
Integer result = multiplyBy5.apply(10); // 50
162
163
// Partial application
164
Function3<String, String, String, String> concat3 = (a, b, c) -> a + b + c;
165
Function2<String, String, String> prefixed = concat3.apply("Hello ");
166
Function1<String, String> greeting = prefixed.apply("World");
167
String message = greeting.apply("!"); // "Hello World!"
168
169
// Lifting functions to work with containers
170
Function1<String, Integer> safeLength = Function1.lift(String::length);
171
Option<String> maybeName = Option.of("John");
172
Option<Integer> maybeLength = safeLength.apply(maybeName); // Some(4)
173
174
// Working with Try
175
Function1<String, Integer> parseToInt = Integer::parseInt;
176
Function1<Try<String>, Try<Integer>> safeParse = Function1.liftTry(parseToInt);
177
Try<String> input = Try.of(() -> "123");
178
Try<Integer> parsed = safeParse.apply(input); // Success(123)
179
```
180
181
### Checked Function Interfaces
182
183
Function interfaces that can throw checked exceptions, providing functional programming with exception handling.
184
185
```java { .api }
186
/**
187
* Supplier that may throw checked exceptions
188
*/
189
@FunctionalInterface
190
interface CheckedFunction0<R> {
191
// Execution
192
R apply() throws Throwable; // Execute function, may throw
193
194
// Conversion to regular functions
195
default Function0<Try<R>> unchecked(); // Convert to Function0 returning Try
196
default Function0<R> recover(Function<? super Throwable, ? extends R> recover);
197
198
// Lifting
199
static <R> CheckedFunction0<R> of(CheckedFunction0<R> function);
200
static <R> CheckedFunction0<R> constant(R value); // Function returning constant value
201
202
// Memoization
203
CheckedFunction0<R> memoized();
204
}
205
206
/**
207
* Single argument function that may throw checked exceptions
208
*/
209
@FunctionalInterface
210
interface CheckedFunction1<T1, R> {
211
// Execution
212
R apply(T1 t1) throws Throwable; // Apply function, may throw
213
214
// Conversion to regular functions
215
default Function1<T1, Try<R>> unchecked(); // Convert to Function1 returning Try
216
default Function1<T1, R> recover(Function<? super Throwable, ? extends R> recover);
217
218
// Lifting
219
static <T1, R> CheckedFunction1<T1, R> of(CheckedFunction1<T1, R> function);
220
static <T1, R> CheckedFunction1<T1, R> identity();
221
static <T1, R> CheckedFunction1<T1, R> constant(R value);
222
223
// Composition (with exception handling)
224
default <V> CheckedFunction1<T1, V> andThen(CheckedFunction1<? super R, ? extends V> after);
225
default <V> CheckedFunction1<V, R> compose(CheckedFunction1<? super V, ? extends T1> before);
226
227
// Memoization
228
CheckedFunction1<T1, R> memoized();
229
}
230
231
/**
232
* Two argument function that may throw checked exceptions
233
*/
234
@FunctionalInterface
235
interface CheckedFunction2<T1, T2, R> {
236
// Execution
237
R apply(T1 t1, T2 t2) throws Throwable; // Apply function, may throw
238
239
// Conversion to regular functions
240
default Function2<T1, T2, Try<R>> unchecked(); // Convert to Function2 returning Try
241
default Function2<T1, T2, R> recover(Function<? super Throwable, ? extends R> recover);
242
243
// Lifting and factory methods
244
static <T1, T2, R> CheckedFunction2<T1, T2, R> of(CheckedFunction2<T1, T2, R> function);
245
static <T1, T2, R> CheckedFunction2<T1, T2, R> constant(R value);
246
247
// Composition
248
default <V> CheckedFunction2<T1, T2, V> andThen(CheckedFunction1<? super R, ? extends V> after);
249
250
// Currying
251
CheckedFunction1<T1, CheckedFunction1<T2, R>> curried();
252
253
// Partial application
254
CheckedFunction1<T2, R> apply(T1 t1);
255
256
// Memoization
257
CheckedFunction2<T1, T2, R> memoized();
258
}
259
260
// ... continues for CheckedFunction3 through CheckedFunction8
261
```
262
263
**Usage Examples:**
264
265
```java
266
import io.vavr.CheckedFunction1;
267
import io.vavr.CheckedFunction2;
268
import io.vavr.control.Try;
269
270
// Checked functions for operations that may throw
271
CheckedFunction1<String, Integer> parseInteger = Integer::parseInt;
272
CheckedFunction1<String, String> readFile = fileName -> Files.readString(Paths.get(fileName));
273
CheckedFunction2<String, String, Void> writeFile = (fileName, content) -> {
274
Files.writeString(Paths.get(fileName), content);
275
return null;
276
};
277
278
// Converting to safe functions
279
Function1<String, Try<Integer>> safeParse = parseInteger.unchecked();
280
Try<Integer> result1 = safeParse.apply("123"); // Success(123)
281
Try<Integer> result2 = safeParse.apply("abc"); // Failure(NumberFormatException)
282
283
// Using recover for error handling
284
Function1<String, Integer> parseWithDefault = parseInteger.recover(ex -> -1);
285
Integer parsed1 = parseWithDefault.apply("123"); // 123
286
Integer parsed2 = parseWithDefault.apply("abc"); // -1
287
288
// Composition with checked functions
289
CheckedFunction1<String, String> processFile = readFile
290
.andThen(content -> content.toUpperCase())
291
.andThen(upper -> upper.replace("OLD", "NEW"));
292
293
Try<String> processed = processFile.unchecked().apply("input.txt");
294
295
// Memoized checked functions
296
CheckedFunction1<String, String> expensiveOperation = input -> {
297
Thread.sleep(1000); // Simulate expensive operation
298
return "Processed: " + input;
299
};
300
CheckedFunction1<String, String> memoized = expensiveOperation.memoized();
301
302
// First call - slow
303
Try<String> result3 = memoized.unchecked().apply("test");
304
// Second call - fast (cached)
305
Try<String> result4 = memoized.unchecked().apply("test");
306
```
307
308
### Predicate and Consumer Interfaces
309
310
Enhanced predicate and consumer interfaces with exception handling and composition.
311
312
```java { .api }
313
/**
314
* Predicate that may throw checked exceptions
315
*/
316
@FunctionalInterface
317
interface CheckedPredicate<T> {
318
// Execution
319
boolean test(T t) throws Throwable; // Test predicate, may throw
320
321
// Conversion to regular predicates
322
default Predicate<T> unchecked(); // Convert to Predicate catching exceptions
323
324
// Logical operations
325
default CheckedPredicate<T> and(CheckedPredicate<? super T> other);
326
default CheckedPredicate<T> or(CheckedPredicate<? super T> other);
327
default CheckedPredicate<T> negate();
328
329
// Factory methods
330
static <T> CheckedPredicate<T> of(CheckedPredicate<T> predicate);
331
}
332
333
/**
334
* Consumer that may throw checked exceptions
335
*/
336
@FunctionalInterface
337
interface CheckedConsumer<T> {
338
// Execution
339
void accept(T t) throws Throwable; // Consume value, may throw
340
341
// Conversion to regular consumer
342
default Consumer<T> unchecked(); // Convert to Consumer catching exceptions
343
344
// Chaining
345
default CheckedConsumer<T> andThen(CheckedConsumer<? super T> after);
346
347
// Factory methods
348
static <T> CheckedConsumer<T> of(CheckedConsumer<T> consumer);
349
}
350
351
/**
352
* Runnable that may throw checked exceptions
353
*/
354
@FunctionalInterface
355
interface CheckedRunnable {
356
// Execution
357
void run() throws Throwable; // Run operation, may throw
358
359
// Conversion to regular Runnable
360
default Runnable unchecked(); // Convert to Runnable catching exceptions
361
362
// Factory methods
363
static CheckedRunnable of(CheckedRunnable runnable);
364
}
365
```
366
367
**Usage Examples:**
368
369
```java
370
import io.vavr.CheckedPredicate;
371
import io.vavr.CheckedConsumer;
372
import io.vavr.CheckedRunnable;
373
374
// Checked predicates
375
CheckedPredicate<String> isValidFile = fileName -> Files.exists(Paths.get(fileName));
376
CheckedPredicate<String> isReadable = fileName -> Files.isReadable(Paths.get(fileName));
377
378
// Combining predicates
379
CheckedPredicate<String> canRead = isValidFile.and(isReadable);
380
Predicate<String> safeCanRead = canRead.unchecked(); // Won't throw, returns false on exception
381
382
// Using in streams
383
List<String> validFiles = fileNames.filter(safeCanRead);
384
385
// Checked consumers
386
CheckedConsumer<String> writeToLog = message -> {
387
Files.write(Paths.get("app.log"), (message + "\n").getBytes(),
388
StandardOpenOption.CREATE, StandardOpenOption.APPEND);
389
};
390
391
CheckedConsumer<String> sendEmail = message -> emailService.send(message);
392
393
// Chaining consumers
394
CheckedConsumer<String> logAndEmail = writeToLog.andThen(sendEmail);
395
Consumer<String> safeProcess = logAndEmail.unchecked();
396
397
// Using with collections
398
messages.forEach(safeProcess);
399
400
// Checked runnable
401
CheckedRunnable startup = () -> {
402
initializeDatabase();
403
loadConfiguration();
404
startServices();
405
};
406
407
// Convert to safe runnable
408
Runnable safeStartup = startup.unchecked();
409
new Thread(safeStartup).start();
410
```
411
412
### Partial Functions
413
414
Functions that are not defined for all possible inputs, providing pattern matching and domain restriction capabilities.
415
416
```java { .api }
417
/**
418
* Function that is not defined for all inputs
419
*/
420
interface PartialFunction<T, R> extends Function1<T, R> {
421
// Factory methods
422
static <T, R> PartialFunction<T, R> of(Predicate<? super T> isDefinedAt,
423
Function<? super T, ? extends R> apply);
424
425
// Domain checking
426
boolean isDefinedAt(T value); // Check if function is defined for value
427
428
// Safe application
429
Option<R> lift(T value); // Apply if defined, return None otherwise
430
431
// Execution (throws if not defined)
432
R apply(T value); // Apply function, throws if not defined at value
433
434
// Composition
435
default <V> PartialFunction<T, V> andThen(Function<? super R, ? extends V> after);
436
default <V> PartialFunction<V, R> compose(PartialFunction<? super V, ? extends T> before);
437
438
// Combination with other partial functions
439
default PartialFunction<T, R> orElse(PartialFunction<? super T, ? extends R> that);
440
441
// Lifting to total function
442
default Function1<T, Option<R>> lift(); // Convert to total function returning Option
443
444
// Pattern matching support
445
static <T, R> PartialFunction<T, R> caseOf(Predicate<? super T> condition,
446
Function<? super T, ? extends R> function);
447
}
448
```
449
450
**Usage Examples:**
451
452
```java
453
import io.vavr.PartialFunction;
454
import io.vavr.control.Option;
455
456
// Creating partial functions
457
PartialFunction<Integer, String> evenToString = PartialFunction.of(
458
n -> n % 2 == 0, // Defined only for even numbers
459
n -> "Even: " + n // Function to apply
460
);
461
462
PartialFunction<Integer, String> oddToString = PartialFunction.of(
463
n -> n % 2 == 1, // Defined only for odd numbers
464
n -> "Odd: " + n // Function to apply
465
);
466
467
// Checking if defined
468
boolean defined = evenToString.isDefinedAt(4); // true
469
boolean notDefined = evenToString.isDefinedAt(3); // false
470
471
// Safe application
472
Option<String> result1 = evenToString.lift(4); // Some("Even: 4")
473
Option<String> result2 = evenToString.lift(3); // None
474
475
// Combining partial functions
476
PartialFunction<Integer, String> numberToString = evenToString.orElse(oddToString);
477
478
// Now defined for all integers
479
String result3 = numberToString.apply(4); // "Even: 4"
480
String result4 = numberToString.apply(3); // "Odd: 3"
481
482
// Pattern matching style
483
PartialFunction<Object, String> classifier = PartialFunction
484
.caseOf(String.class::isInstance, obj -> "String: " + obj)
485
.orElse(PartialFunction.caseOf(Integer.class::isInstance, obj -> "Integer: " + obj))
486
.orElse(PartialFunction.caseOf(o -> true, obj -> "Other: " + obj.getClass().getSimpleName()));
487
488
String classification1 = classifier.apply("hello"); // "String: hello"
489
String classification2 = classifier.apply(42); // "Integer: 42"
490
String classification3 = classifier.apply(3.14); // "Other: Double"
491
492
// Converting to total function
493
Function1<Integer, Option<String>> totalFunction = evenToString.lift();
494
Option<String> maybeResult = totalFunction.apply(5); // None
495
496
// Using in collection processing
497
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
498
List<String> evenStrings = numbers
499
.map(evenToString.lift()) // Apply partial function safely
500
.filter(Option::isDefined) // Keep only defined results
501
.map(Option::get); // Extract values
502
// Result: ["Even: 2", "Even: 4", "Even: 6"]
503
```