जावा डिफ़ॉल्ट विधि उपयोग


13

दशकों से यह मामला है कि इंटरफेस थे गया है केवल केवल (केवल) विधि हस्ताक्षर निर्दिष्ट करने के लिए। हमें बताया गया कि यह "चीजों को करने का सही तरीका ™" था।

तब जावा 8 बाहर आया और कहा:

खैर, एर, उह, अब आप डिफ़ॉल्ट तरीकों को परिभाषित कर सकते हैं। होगा चला, अलविदा।

मैं उत्सुक हूं कि यह कैसे दोनों अनुभवी जावा डेवलपर्स और जो हाल ही में (पिछले कुछ वर्षों से) शुरू कर रहे हैं, वे इसे विकसित कर रहे हैं। मैं यह भी सोच रहा हूं कि यह जावा ऑर्थोडॉक्सी और अभ्यास में कैसे फिट बैठता है।

मैं कुछ प्रायोगिक कोड बना रहा हूं और जब मैं कुछ रीफैक्टरिंग कर रहा था, तो मैंने एक इंटरफ़ेस के साथ समाप्त किया, जो बस एक मानक इंटरफ़ेस (Iterable) का विस्तार करता है और दो डिफ़ॉल्ट तरीकों को जोड़ता है। और मैं ईमानदार रहूँगा, मुझे बहुत अच्छा लग रहा है।

मुझे पता है कि यह थोड़ा खुला हुआ है लेकिन अब जब जावा 8 को वास्तविक परियोजनाओं में इस्तेमाल होने में कुछ समय हो गया है, तो क्या अभी तक डिफ़ॉल्ट तरीकों के उपयोग के आसपास एक रूढ़िवादी है? जब मैं चर्चा कर रहा होता हूं, तो ज्यादातर ऐसे होते हैं कि मौजूदा उपभोक्ताओं को तोड़े बिना एक इंटरफेस में नए तरीकों को कैसे जोड़ा जाए। लेकिन शुरू से इसका उपयोग करने के बारे में क्या मैंने ऊपर दिए गए उदाहरण की तरह। क्या किसी ने अपने इंटरफेस में कार्यान्वयन प्रदान करने के साथ किसी भी मुद्दे में भाग लिया है?


मुझे भी इस परिप्रेक्ष्य में दिलचस्पी होगी। मैं .Net दुनिया में 6 साल बाद जावा में लौट रहा हूं। यह मुझे लगता है कि यह रूबी के मॉड्यूल तरीकों से थोड़ा प्रभाव के साथ, सी # विस्तार विधियों के लिए जावा का जवाब हो सकता है। मैंने इसके साथ नहीं खेला है, इसलिए मुझे यकीन नहीं हो रहा है।
बेरिन लोरिट्श

1
मुझे लगता है कि इस कारण से कि वे डिफ़ॉल्ट तरीके जोड़ते हैं, काफी हद तक इतना है कि वे पूरी तरह से अलग इंटरफेस बनाने के बिना संग्रह इंटरफेस का विस्तार कर सकते हैं
जस्टिन

1
@ जस्टिन: java.util.function.Functionएक नए इंटरफ़ेस में डिफ़ॉल्ट तरीकों के उपयोग के लिए देखें ।
जोर्ग डब्ल्यू मित्तग

@ जस्टिन मेरा अनुमान है कि यह मुख्य चालक था। मुझे वास्तव में प्रक्रिया पर फिर से ध्यान देना शुरू करना चाहिए क्योंकि उन्होंने वास्तव में बदलाव करना शुरू कर दिया है।
जिमीजम्स

जवाबों:


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

यह लैम्ब्डा के साथ संयोजन में बहुत दिलचस्प हो जाता है, क्योंकि इस तरह के "लीवर" इंटरफ़ेस को लैम्ब्डा (यह एसएएम इंटरफ़ेस है) द्वारा लागू किया जा सकता है।

यह वही उपयोग-केस है जिसे C but में एक्सटेंशन मेथड्स के लिए जोड़ा गया था, लेकिन डिफ़ॉल्ट तरीकों का एक अलग फायदा है: वे "उचित" उदाहरण के तरीके हैं, जिसका अर्थ है कि उनके पास इंटरफ़ेस के निजी कार्यान्वयन विवरण तक पहुंच है ( privateइंटरफ़ेस मेथड आ रहे हैं जावा 9 में), जबकि एक्सटेंशन मेथड्स स्टैटिक मेथड के लिए सिंटैक्टिक शुगर हैं।

जावा को कभी इंटरफ़ेस इंजेक्शन मिलना चाहिए, यह टाइप-सेफ, स्कोप्ड, मॉड्यूलर बंदर-पैचिंग की भी अनुमति देगा। यह JVM पर भाषा कार्यान्वयनकर्ताओं के लिए बहुत दिलचस्प होगा: फिलहाल, उदाहरण के लिए, JRuby या तो अतिरिक्त रूबी शब्दार्थ के साथ प्रदान करने के लिए जावा कक्षाओं से विरासत में लेता है या उन्हें लपेटता है, लेकिन आदर्श रूप से, वे उसी कक्षाओं का उपयोग करना चाहते हैं। इंटरफ़ेस इंजेक्शन के तरीके के साथ और चूक, वे इंजेक्षन सकता है जैसे एक RubyObjectइंटरफेस में java.lang.Objectहै, ताकि एक जावा Objectऔर एक रूबी Objectहैं सटीक एक ही बात


1
मैं इसका पूरी तरह से पालन नहीं करता हूं। इंटरफ़ेस पर डिफ़ॉल्ट विधि को अन्य तरीकों के संदर्भ में या ऑब्जेक्ट में परिभाषित तरीकों पर परिभाषित किया जाना है। क्या आप एक उदाहरण दे सकते हैं कि आप डिफ़ॉल्ट विधि के साथ सार्थक एकल विधि इंटरफ़ेस कैसे बनाते हैं? यदि आपको प्रदर्शित करने के लिए जावा 9 सिंटैक्स की आवश्यकता है, तो यह ठीक है।
जिमीजम्स

उदाहरण के लिए: एक Comparableएक सार के साथ इंटरफेस compareToविधि, और डिफ़ॉल्ट lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual, isBetween, और clampविधियों, सभी के मामले में लागू किया compareTo। या, बस देखें java.util.function.Function: इसमें एक अमूर्त applyविधि और दो डिफ़ॉल्ट रचना विधियां हैं, दोनों के संदर्भ में लागू किया गया है apply। मैंने एक Collectionइंटरफ़ेस का उदाहरण देने की कोशिश की , लेकिन यह सब टाइप-सुरक्षित होना मुश्किल है और इस जवाब के लिए बहुत लंबा है - मैं एक गैर-टाइप-सुरक्षित, गैर-टाइप-संरक्षण संस्करण को एक शॉट देने की कोशिश करूंगा। बने रहें।
जोर्ग डब्ल्यू मित्तग

3
उदाहरण मदद करते हैं। धन्यवाद। मैंने गलत समझा कि आपको सिंगल मेथड इंटरफेस से क्या मतलब है।
जिमीजैम

डिफ़ॉल्ट तरीकों का क्या मतलब है कि एक एकल सार विधि इंटरफ़ेस में अब एक एकल विधि इंटरफ़ेस नहीं है ;-)
Jörg W Mittag

मैं इस बारे में सोच रहा था और मेरे साथ यह हुआ कि AbstractCollection और AbstractList मूल रूप से आप यहाँ किस बारे में बात कर रहे हैं (1 के बजाय 2 विधि, लेकिन मुझे नहीं लगता कि यह महत्वपूर्ण है।) यदि ये अवशिष्ट तरीकों के साथ इंटरफेस के रूप में पुनरावर्ती थे, तो यह। आकार को जोड़कर एक संग्रह में चलने के लिए चलने योग्य बनाने के लिए सुपर सरल होना और किसी भी चीज़ से एक सूची बनाना भी एक तस्वीर है यदि आप आकार को अनुक्रमित और जान सकते हैं।
जिमीजम्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.