การใช้วิธีการเริ่มต้นของ Java


13

สำหรับทศวรรษที่ผ่านมาจะได้รับกรณีที่อินเตอร์เฟซได้เพียง เท่านั้น (เท่านั้น) สำหรับการระบุลายเซ็นวิธี เราได้รับการบอกว่านี่เป็น "วิธีการทำสิ่งที่ถูกต้อง"

จากนั้น Java 8 ออกมาและพูดว่า:

เอ่อเอ่อตอนนี้คุณสามารถกำหนดวิธีการเริ่มต้น ต้องวิ่งลาก่อน

ฉันอยากรู้ว่าสิ่งนี้ถูกย่อยโดยนักพัฒนา Java ที่มีประสบการณ์และผู้ที่เริ่มเมื่อเร็ว ๆ นี้ (ไม่กี่ปีที่ผ่านมา) พัฒนามัน ฉันยังสงสัยเกี่ยวกับความเหมาะสมของ Java Orthodoxy และการฝึกฝนอย่างไร

ฉันกำลังสร้างรหัสทดลองและในขณะที่ฉันกำลังทำการปรับเปลี่ยนใหม่ฉันลงเอยด้วยอินเทอร์เฟซที่ขยายส่วนต่อประสานมาตรฐาน (Iterable) และเพิ่มวิธีการเริ่มต้นสองวิธี และฉันจะซื่อสัตย์ฉันรู้สึกดีกับมัน

ฉันรู้ว่านี่เป็นแบบปลายเปิดเล็ก ๆ น้อย ๆ แต่ตอนนี้มีเวลาสำหรับ Java 8 ที่จะใช้ในโครงการจริงมี orthodoxy รอบการใช้วิธีการเริ่มต้นหรือยัง สิ่งที่ฉันเห็นเป็นส่วนใหญ่เมื่อพูดถึงคือวิธีการเพิ่มวิธีการใหม่ในอินเทอร์เฟซโดยไม่ทำลายผู้บริโภค แต่สิ่งที่เกี่ยวกับการใช้สิ่งนี้ตั้งแต่เริ่มต้นเหมือนตัวอย่างที่ฉันให้ไว้ข้างต้น มีใครประสบปัญหาใด ๆ กับการให้การใช้งานในส่วนต่อประสานของพวกเขา?


ฉันยังสนใจในมุมมองนี้ ฉันกลับมาที่ Java หลังจาก 6 ปีในโลก. Net ฉันว่านี่อาจเป็นคำตอบของ Java สำหรับวิธีการขยาย C # ด้วยอิทธิพลเล็กน้อยจากวิธีการโมดูลรูบี้ ฉันไม่ได้เล่นกับมันดังนั้นฉันจึงไม่แน่ใจ
Berin Loritsch

1
ฉันรู้สึกเหมือนเหตุผลว่าทำไมพวกเขาเพิ่มวิธีการเริ่มต้นเป็นส่วนใหญ่เพื่อให้พวกเขาสามารถขยายอินเทอร์เฟซการรวบรวมโดยไม่ต้องสร้างอินเทอร์เฟซต่าง ๆ ทั้งหมด
Justin

1
@Justin: ดูjava.util.function.Functionการใช้งานวิธีการเริ่มต้นในอินเทอร์เฟซใหม่
Jörg W Mittag

@ จัสตินฉันเดาว่านี่เป็นตัวขับเคลื่อนหลัก ฉันควรเริ่มให้ความสนใจกับกระบวนการอีกครั้งเพราะพวกเขาเริ่มทำการเปลี่ยนแปลงจริงๆ
JimmyJames

คำตอบ:


12

กรณีใช้งานที่ยอดเยี่ยมคือสิ่งที่ฉันเรียกว่า "คาน" อินเทอร์เฟซ: อินเทอร์เฟซที่มีวิธีนามธรรมจำนวนน้อย (นึกคิด 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เป็นสิ่งเดียวที่แน่นอน


1
ฉันไม่ได้ติดตามอย่างเต็มที่ ต้องกำหนดวิธีการเริ่มต้นบนอินเทอร์เฟซในแง่ของวิธีการอื่น ๆ ในอินเทอร์เฟซหรือวิธีการที่กำหนดไว้ในวัตถุ คุณสามารถยกตัวอย่างวิธีสร้างอินเทอร์เฟซวิธีเดียวที่มีความหมายด้วยวิธีการเริ่มต้นได้หรือไม่ หากคุณต้องการไวยากรณ์ Java 9 เพื่อแสดงว่าไม่เป็นไร
JimmyJames

ยกตัวอย่างเช่นที่: Comparableอินเตอร์เฟซกับนามธรรมcompareToวิธีการและเริ่มต้นlessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual, isBetweenและวิธีการดำเนินการทั้งหมดในแง่ของclamp compareToหรือเพียงแค่มองไปที่java.util.function.Functionมันมีความเป็นนามธรรมวิธีการและสองวิธีองค์ประกอบเริ่มต้นทั้งการดำเนินการในแง่ของapply applyฉันพยายามยกตัวอย่างของCollectionอินเทอร์เฟซ แต่การทำให้ทุกอย่างปลอดภัยเป็นเรื่องที่ยุ่งยากและนานเกินไปสำหรับคำตอบนี้ - ฉันจะพยายามให้ช็อตช็อตที่ไม่ปลอดภัยและไม่รักษาประเภท คอยติดตาม.
Jörg W Mittag

3
ตัวอย่างช่วยเหลือ ขอบคุณ ฉันเข้าใจผิดว่าคุณหมายถึงอะไรโดยอินเทอร์เฟซวิธีการเดียว
JimmyJames

หมายความว่าวิธีการเริ่มต้นคืออินเทอร์เฟซวิธีนามธรรมเดียวไม่จำเป็นต้องเป็นอินเทอร์เฟซวิธีเดียวอีกต่อไป
Jörg W Mittag

ฉันคิดเกี่ยวกับเรื่องนี้และมันเกิดขึ้นกับฉันที่ AbstractCollection และ AbstractList นั้นเป็นสิ่งที่คุณกำลังพูดถึงที่นี่ (2 วิธีแทนที่จะเป็น 1 แต่ฉันไม่คิดว่ามันสำคัญ) ถ้าสิ่งเหล่านี้เป็นรูปแบบอินเทอร์เฟซใหม่ ง่ายสุด ๆ ในการเปลี่ยน iterable ให้เป็นคอลเล็กชันโดยการเพิ่มขนาดและสร้างรายการจากอะไรก็ได้เช่นกันถ้าคุณสามารถจัดทำดัชนีและรู้ขนาดได้
JimmyJames
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.