กรณีใช้งานที่ยอดเยี่ยมคือสิ่งที่ฉันเรียกว่า "คาน" อินเทอร์เฟซ: อินเทอร์เฟซที่มีวิธีนามธรรมจำนวนน้อย (นึกคิด 1) แต่ให้ "ยกระดับ" จำนวนมากในการที่พวกเขาให้ฟังก์ชั่นมากมาย: คุณเท่านั้น จำเป็นต้องใช้ 1 วิธีในชั้นเรียนของคุณ แต่รับวิธีอื่น ๆ อีกมากมาย "ฟรี" คิดว่าของอินเตอร์เฟซคอลเลกชันเช่นกับนามธรรมเดียวforeach
วิธีการและdefault
วิธีการเช่นmap
, fold
, reduce
, filter
, partition
, groupBy
, sort
, sortBy
ฯลฯ
นี่คือตัวอย่างบางส่วน เริ่มjava.util.function.Function<T, R>
กันเลย R apply<T>
แต่ก็มีวิธีนามธรรมเดียว และมันมีวิธีการเริ่มต้นสองวิธีที่ช่วยให้คุณสามารถเขียนฟังก์ชันด้วยฟังก์ชันอื่นได้สองวิธีก่อนหรือหลัง วิธีการจัดองค์ประกอบทั้งสองนั้นมีการใช้งานโดยใช้เพียงapply
:
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));
}
คุณสามารถสร้างอินเทอร์เฟซสำหรับวัตถุที่เทียบเคียงได้ดังนี้:
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());
}
}
หรือเฟรมเวิร์กคอลเล็กชันที่ง่ายที่สุดซึ่งการดำเนินการรวบรวมทั้งหมดกลับมาCollection
โดยไม่คำนึงถึงประเภทดั้งเดิม:
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)
}
}
สิ่งนี้น่าสนใจมากเมื่อใช้ร่วมกับ lambdas เนื่องจากอินเตอร์เฟสดังกล่าวสามารถใช้งานได้โดยแลมบ์ดา (เป็นอินเตอร์เฟส SAM)
นี่เป็นกรณีใช้งานแบบเดียวกันกับที่มีการเพิ่มวิธีการขยายไว้ในC♯ แต่วิธีการเริ่มต้นมีข้อดีที่แตกต่างกันอย่างหนึ่ง: เป็นวิธีการที่เหมาะสม "ซึ่งหมายความว่าพวกเขาสามารถเข้าถึงรายละเอียดการใช้งานส่วนตัวของอินprivate
เทอร์เฟซ ใน Java 9) ในขณะที่วิธีการขยายเป็นเพียงน้ำตาล syntactic สำหรับวิธีการคงที่
หาก Java เคยได้รับการฉีดอินเทอร์เฟซก็จะช่วยให้ลิงปลอดภัยชนิด, ขอบเขต, modular patching สิ่งนี้น่าสนใจมากสำหรับผู้พัฒนาภาษาใน JVM: ในขณะนี้ JRuby อาจสืบทอดมาจากหรือตัดคลาส Java เพื่อให้พวกเขามีความหมายเพิ่มเติมของ Ruby แต่พวกเขาต้องการใช้คลาสเดียวกัน ด้วยอินเตอร์เฟซและการฉีดเริ่มต้นวิธีการที่พวกเขาสามารถฉีดเช่นRubyObject
อินเตอร์เฟซที่เข้ามาjava.lang.Object
เพื่อให้ Java Object
และทับทิมObject
เป็นสิ่งเดียวที่แน่นอน