As said in this answer, allowing to create interfaces with more than one method still being functional interfaces, is one of the purposes of default methods. As also mentioned there, you will find examples within the Java API itself, say Comparator
, Predicate
, or Function
, having default
methods and intentionally being functional interfaces.
It doesn’t matter whether the default
implementation is doing nothing or not, the more important question is, how natural is this default implementation. Does it feel like a kludge, just to make lambdas possible, or is it indeed what some or even most implementations would use any way (regardless of how they are implemented)?
Not needing a special clean up action might be indeed a common case, even if you follow the suggestion made in a comment, to let your interface extend AutoCloseable
and name the method close
instead of end
. Note that likewise, Stream
implements AutoCloseable
and its default behavior is to do nothing on close()
. You could even follow the pattern to allow specifying the cleanup action as separate Runnable
, similar to Stream.onClose(Runnable)
:
public interface StringTransformer extends UnaryOperator<String>, AutoCloseable {
static StringTransformer transformer(Function<String,String> f) {
return f::apply;
}
String transform(String s);
@Override default String apply(String s) { return transform(s); }
@Override default void close() {}
default StringTransformer onClose(Runnable r) {
return new StringTransformer() {
@Override public String transform(String s) {
return StringTransformer.this.transform(s);
}
@Override public void close() {
try(StringTransformer.this) { r.run(); }
}
};
}
}
This allows registering a cleanup action via onClose
, so the following works:
try(StringTransformer t =
StringTransformer.transformer(String::toUpperCase)
.onClose(()->System.out.println("close"))) {
System.out.println(t.apply("some text"));
}
resp.
try(StringTransformer t = transformer(String::toUpperCase)
.onClose(()->System.out.println("close 1"))) {
System.out.println(t.apply("some text"));
}
if you use import static
. It also ensures safe closing if you chain multiple actions like
try(StringTransformer t = transformer(String::toUpperCase)
.onClose(()->System.out.println("close 1"))
.onClose(()->{ throw new IllegalStateException(); })) {
System.out.println(t.apply("some text"));
}
or
try(StringTransformer t = transformer(String::toUpperCase)
.onClose(()->{ throw new IllegalStateException("outer fail"); })
.onClose(()->{ throw new IllegalStateException("inner fail"); })){
System.out.println(t.apply("some text"));
}
Note that try(StringTransformer.this) { r.run(); }
is Java 9 syntax. For Java 8, you would need try(StringTransformer toClose = StringTransformer.this) { r.run(); }
.