Java Varsayılan Yöntem Kullanımı


13

Onlarca yıldır arayüzler olduğu durumda oldu sadece tek yöntem imzaları belirtmek için (yalnızca). Bize bunun "bir şeyler yapmanın doğru yolu" olduğu söylendi.

Sonra Java 8 çıktı ve şöyle dedi:

Şimdi, varsayılan yöntemleri tanımlayabilirsiniz. Koşmalıyım, güle güle.

Bunun hem deneyimli Java geliştiricileri hem de son zamanlarda (son birkaç yıl) geliştirmeye başlayanlar tarafından nasıl sindirildiğini merak ediyorum. Bunun Java ortodoksluğuna ve pratiğine nasıl uyduğunu merak ediyorum.

Bazı deneysel kod inşa ediyorum ve bazı yeniden düzenleme yaparken, ben sadece standart bir arayüz (Yinelenebilir) genişleten ve iki varsayılan yöntem ekleyen bir arayüz ile sona erdi. Ve dürüst olacağım, bu konuda kendimi çok iyi hissediyorum.

Bunun biraz açık uçlu olduğunu biliyorum, ancak Java 8'in gerçek projelerde kullanılması için biraz zaman geçtiğine göre, henüz varsayılan yöntemlerin kullanımı konusunda bir ortodoks var mı? Tartışıldıklarında en çok gördüğüm şey, mevcut tüketicileri bozmadan bir arayüze nasıl yeni yöntemler ekleneceğidir. Ama bunu en başta yukarıda verdiğim örnek gibi kullanmaya ne dersiniz? Arayüzlerinde uygulama sağlama konusunda herhangi bir sorunla karşılaşan var mı?


Ben de bu bakış açısıyla ilgilenirim. .Net dünyasında 6 yıl sonra Java'ya dönüyorum. Bana göre bu, Java'nın C # uzatma yöntemleri için cevabı, Ruby'nin modül yöntemlerinden biraz etkisi olabilir. Onunla oynamadım, bu yüzden emin olamıyorum.
Berin Loritsch

1
Varsayılan yöntemleri eklediklerinin sebebinin büyük ölçüde toplama arayüzlerini tamamen farklı arayüzler yapmak zorunda kalmadan genişletebilmelerini hissediyorum
Justin

1
@ Justin: java.util.function.Functionyepyeni bir arayüzde varsayılan yöntemlerin kullanımı için bakınız .
Jörg W Mittag

@Justin Sanırım bu ana sürücü oldu. Gerçekten değişiklik yapmaya başladığı için sürece tekrar dikkat etmeye başlamalıyım.
JimmyJames

Yanıtlar:


12

Harika bir kullanım örneği "kaldıraç" arayüzler olarak adlandırdığım şeylerdir: sadece az sayıda soyut yönteme (ideal olarak 1) sahip olan, ancak size çok fazla işlevsellik sağladıkları için çok fazla "kaldıraç" sağlayan arayüzler: sınıfınızda 1 yöntem uygulamanız gerekir, ancak "ücretsiz" olarak başka birçok yöntem edinmeniz gerekir. Tek bir özet, örneğin bir toplama arayüzü düşünün foreachyöntem ve defaultyöntemler gibi map, fold, reduce, filter, partition, groupBy, sort, sortBy, vs.

Burada bir çift örnek var. İle başlayalım java.util.function.Function<T, R>. Tek bir soyut yöntemi vardır R apply<T>. Önceden veya sonra, başka bir işlevle işlevi iki farklı şekilde oluşturmanıza izin veren iki varsayılan yöntem vardır. Bu kompozisyon yöntemlerinin her ikisi de sadece kullanılarak uygulanırapply :

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));
}

Karşılaştırılabilir nesneler için aşağıdaki gibi bir arayüz de oluşturabilirsiniz:

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());
  }
}

Veya Collectionorijinal türün ne olduğuna bakılmaksızın tüm koleksiyon işlemlerinin döndüğü son derece basitleştirilmiş bir koleksiyon çerçevesi :

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)
  }
}

Bu lambdalarla birlikte çok ilginç hale gelir, çünkü böyle bir "kaldıraç" arayüzü bir lambda tarafından uygulanabilir (bir SAM arayüzüdür).

Bu, Uzatma Yöntemlerinin C♯'de eklendiği kullanım örneğidir, ancak varsayılan yöntemlerin ayrı bir avantajı vardır: "uygun" örnek yöntemleridir, yani arayüzün özel uygulama ayrıntılarına erişimleri vardır ( privatearayüz yöntemleri geliyor) Java 9'da), oysa Uzatma Yöntemleri sadece statik yöntemler için sözdizimsel şekerdir.

Java hiç Arayüz Enjeksiyonu alırsa, aynı zamanda tip güvenli, kapsamlı, modüler maymun yamalamaya da izin verir. Bu, JVM'deki dil uygulayıcıları için çok ilginç olurdu: şu anda, JRuby, Java sınıflarını ek Ruby semantiği sağlamak için miras alır veya sarar, ancak ideal olarak aynı sınıfları kullanmak isterler. Yöntemler Arayüz Enjeksiyon ile ve Standart bunlar örneğin bir enjekte edebilir RubyObjectarayüz içine java.lang.Object, yani bir Java olduğunu Objectve Ruby Objectolan tam aynı şey .


1
Bunu tam olarak takip etmiyorum. Arabirimdeki varsayılan yöntem, arabirimdeki diğer yöntemler veya Nesne'de tanımlanan yöntemler açısından tanımlanmalıdır. Varsayılan bir yöntemle anlamlı tek yöntem arabirimi nasıl oluşturduğunuza bir örnek verebilir misiniz? Göstermek için Java 9 sözdizimine ihtiyacınız varsa, sorun değil.
JimmyJames

Örneğin: bir Comparablesoyut ile ara compareToyöntemi ve varsayılan lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual, isBetween, ve clampyöntem, her açısından uygulanan compareTo. Veya sadece şuna bakın java.util.function.Function: applyher ikisi de açısından uygulanan soyut bir yöntem ve iki varsayılan kompozisyon yöntemi vardır apply. Bir Collectionarabirim örneği vermeye çalıştım , ancak her şeyin tip-güvenli olmasını sağlamak zor ve bu cevap için çok uzun - tip-güvenli olmayan, tip-koruyucu olmayan bir versiyon vermeye çalışacağım. Bizi izlemeye devam edin.
Jörg W Mittag

3
Örnekler yardımcı olur. Teşekkürler. Tek yöntem arayüzü ile ne demek istediğinizi yanlış anladım.
JimmyJames

Varsayılan yöntemlerin anlamı, tek bir soyut yöntem arabiriminin artık tek bir yöntem arabirimi olması gerekmemesidir ;-)
Jörg W Mittag

Bunu düşünüyordum ve AbstractCollection ve AbstractList'in temelde burada bahsettiğiniz şey olduğu ortaya çıktı (1 yerine 2 yöntem ama bunun çok önemli olduğunu düşünmüyorum.) Bunlar defualt yöntemleriyle arayüz olarak yeniden olsaydı, boyut ekleyerek ve herhangi bir şeyden bir liste oluşturarak bir yinelenebilir bir toplama dönüştürmek için süper basit olması, boyutu endeksleyebilir ve bilirseniz de bir çırpıda.
JimmyJames
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.