Ein großartiger Anwendungsfall sind die von mir als "Hebel" bezeichneten Schnittstellen: Schnittstellen, die nur über eine geringe Anzahl abstrakter Methoden verfügen (idealerweise 1), aber eine Menge "Hebel" bieten, indem sie Ihnen eine Menge Funktionalität bieten: Sie allein Müssen Sie 1 Methode in Ihrer Klasse implementieren, aber erhalten Sie viele andere Methoden "kostenlos". Denken Sie an einer Sammlung Schnittstelle, zum Beispiel mit einem einzigen abstrakten foreach
Verfahren und default
Methoden wie map
, fold
, reduce
, filter
, partition
, groupBy
, sort
, sortBy
, usw.
Hier einige Beispiele. Beginnen wir mit java.util.function.Function<T, R>
. Es gibt nur eine abstrakte Methode R apply<T>
. Es gibt zwei Standardmethoden, mit denen Sie die Funktion mit einer anderen Funktion auf zwei verschiedene Arten zusammenstellen können, entweder vorher oder nachher. Beide dieser Kompositionsmethoden werden unter Verwendung von nur implementiertapply
:
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
return (T t) -> after.apply(apply(t));
}
Sie können auch eine Schnittstelle für vergleichbare Objekte erstellen, etwa wie folgt:
interface MyComparable<T extends MyComparable<T>> {
int compareTo(T other);
default boolean lessThanOrEqual(T other) {
return compareTo(other) <= 0;
}
default boolean lessThan(T other) {
return compareTo(other) < 0;
}
default boolean greaterThanOrEqual(T other) {
return compareTo(other) >= 0;
}
default boolean greaterThan(T other) {
return compareTo(other) > 0;
}
default boolean isBetween(T min, T max) {
return greaterThanOrEqual(min) && lessThanOrEqual(max);
}
default T clamp(T min, T max) {
if (lessThan( min)) return min;
if (greaterThan(max)) return max;
return (T)this;
}
}
class CaseInsensitiveString implements MyComparable<CaseInsensitiveString> {
CaseInsensitiveString(String s) { this.s = s; }
private String s;
@Override public int compareTo(CaseInsensitiveString other) {
return s.toLowerCase().compareTo(other.s.toLowerCase());
}
}
Oder ein extrem vereinfachtes Auflistungsframework, in dem alle Auflistungsvorgänge Collection
unabhängig vom ursprünglichen Typ zurückgegeben werden:
interface MyCollection<T> {
void forEach(java.util.function.Consumer<? super T> f);
default <R> java.util.Collection<R> map(java.util.function.Function<? super T, ? extends R> f) {
java.util.Collection<R> l = new java.util.ArrayList();
forEach(el -> l.add(f.apply(el)));
return l;
}
}
class MyArray<T> implements MyCollection<T> {
private T[] array;
MyArray(T[] array) { this.array = array; }
@Override public void forEach(java.util.function.Consumer<? super T> f) {
for (T el : array) f.accept(el);
}
@Override public String toString() {
StringBuilder sb = new StringBuilder("(");
map(el -> el.toString()).forEach(s -> { sb.append(s); sb.append(", "); } );
sb.replace(sb.length() - 2, sb.length(), ")");
return sb.toString();
}
public static void main(String... args) {
MyArray<Integer> array = new MyArray<>(new Integer[] {1, 2, 3, 4});
System.out.println(array);
// (1, 2, 3, 4)
}
}
Dies wird in Kombination mit Lambdas sehr interessant, da eine solche "Hebel" -Schnittstelle von einem Lambda implementiert werden kann (es ist eine SAM-Schnittstelle).
Dies ist der gleiche Anwendungsfall, für den Erweiterungsmethoden in C♯ hinzugefügt wurden, aber Standardmethoden haben einen eindeutigen Vorteil: Sie sind "richtige" Instanzmethoden, dh sie haben Zugriff auf private Implementierungsdetails der Schnittstelle ( private
Schnittstellenmethoden werden bereitgestellt) in Java 9), während Erweiterungsmethoden nur syntaktischer Zucker für statische Methoden sind.
Sollte Java jemals Interface Injection bekommen, würde es auch typsicheres, modulares Affen-Patching ermöglichen. Dies wäre für Sprachimplementierer in der JVM sehr interessant: Im Moment übernimmt JRuby beispielsweise entweder Java-Klassen oder bricht sie um, um ihnen zusätzliche Ruby-Semantik bereitzustellen. Idealerweise möchten sie jedoch dieselben Klassen verwenden. Mit Injection - Schnittstelle und Standard Methods, könnten sie injizieren zB eine RubyObject
Schnittstelle in java.lang.Object
, so dass ein Java Object
und Ruby Object
die ist genau die gleiche Sache .