2015-09-14 11 views
20

मेरे पास एक कन्डर्रम है जिसने मुझे इस बात पर विचार करने के लिए प्रेरित किया है कि Collection<T> को लागू किए बिना Iterable<T> लागू करने वाले मानक मानक क्लास हैं या नहीं। मैं एक इंटरफ़ेस को कार्यान्वित कर रहा हूं जिसके लिए मुझे Iterable<T> स्वीकार करने वाली विधि को परिभाषित करने की आवश्यकता है, लेकिन इस विधि को वापस करने के लिए मैं जिस ऑब्जेक्ट का उपयोग कर रहा हूं उसे Collection<T> की आवश्यकता है।क्या कोई जावा मानक वर्ग है जो संग्रह को लागू किए बिना इटरबल लागू करता है?

यह मुझे कुछ वास्तव में क्लूजी महसूस करने वाला कोड कर रहा है जो संकलित होने पर कुछ अनचेक चेतावनियां देता है।

public ImmutableMap<Integer, Optional<Site>> loadAll(
     Iterable<? extends Integer> keys 
) throws Exception { 
    Collection<Integer> _keys; 
    if (keys instanceof Collection) { 
     _keys = (Collection<Integer>) keys; 
    } else { 
     _keys = Lists.newArrayList(keys); 
    } 

    final List<Site> sitesById = siteDBDao.getSitesById(_keys); 
    // snip: convert the list to a map 

अधिक generified Collection<? extends Integer> प्रकार का उपयोग करने के लिए कि लाइन के लिए अनियंत्रित चेतावनी को समाप्त नहीं करती मेरी जिसके परिणामस्वरूप संग्रह बदल रहा है। साथ ही, मैं Iterable के बजाय Collection को स्वीकार करने के लिए विधि हस्ताक्षर नहीं बदल सकता क्योंकि तब यह सुपर विधि को ओवरराइड नहीं कर रहा है और आवश्यकता होने पर कॉल नहीं किया जाएगा।

वहां doesn't seem to be a way around इस कास्ट-या-कॉपी समस्या: अन्य प्रश्न यहां कहीं और पूछे गए हैं और यह जावा के जेनेरिक और टाइप एरर सिस्टम में गहराई से दिखता है। लेकिन मैं इसके बजाय पूछ रहा हूं कि क्या कोई कक्षाएं हैं जो Iterable<T> को कार्यान्वित कर सकती हैं जो Collection<T> को भी लागू नहीं करती हैं? मैंने Iterable JavaDoc के माध्यम से एक नज़र डाली है और निश्चित रूप से जो कुछ भी मैं अपने इंटरफ़ेस में पारित होने की अपेक्षा करता हूं वह वास्तव में एक संग्रह होगा। मैं इसके बजाय एक जंगली, पूर्व-लिखित कक्षा का उपयोग करना चाहता हूं क्योंकि वास्तव में पैरामीटर के रूप में पारित होने की अधिक संभावना होती है और इकाई परीक्षण को और अधिक मूल्यवान बना देता है।

मुझे यकीन है कि मैंने जो कुछ यूनिट परीक्षण लिख रहे हैं, उसके कारण मैं अपने प्रोजेक्ट में उपयोग किए जाने वाले प्रकारों के साथ काम करता हूं। लेकिन मैं कुछ इनपुट के लिए एक यूनिट टेस्ट लिखना चाहता हूं कि एक पुनरावृत्त अभी तक संग्रह नहीं है और अब तक मैं सभी के साथ आने में सक्षम हूं, जो मुझे डमी-टेस्ट क्लास कार्यान्वयन को कार्यान्वित कर रहा है।


उत्सुक के लिए, विधि कार्यान्वयन के लिए मैं कर रहा हूँ अमरूद के CacheLoader<K, V>.loadAll(Iterable<? extends K> keys) है और समर्थन विधि एक JDBI instantiated डेटा का उपयोग वस्तु है, जो एक संग्रह की आवश्यकता है @BindIn इंटरफेस के लिए पैरामीटर प्रकार के रूप में इस्तेमाल किया जा रहा है। मुझे लगता है कि मैं सोचने में सही हूं कि यह सवाल के लिए स्पर्शपूर्ण है, लेकिन अगर कोई मेरी समस्या पर पार्श्व सोचने की कोशिश करता है। मुझे पता है मैं सिर्फ JDBI परियोजना कांटा और @BindIn एनोटेशन पुनर्लेखन एक iterable स्वीकार करने के लिए कर सकता है हूँ ...

+5

अपने शीर्षक में सवाल का जवाब करने के लिए। – aioobe

+1

एक प्रतिलिपि बनाने में क्या गलत है- यानी, नया संग्रह बनाना? आप केवल नई वस्तुओं को नहीं बनाते, संदर्भों की प्रतिलिपि बनायेंगे। – VGR

+0

कुछ भी गलत नहीं है, लेकिन मैं किसी भी आवंटन से बचने के लिए एक रास्ता तलाश रहा था। जैसा कि नाम का तात्पर्य है, ['Lists.newArrayList'] (http://grepcode.com/file/repo1.maven.org/maven2/com.google.guava/guava/r06/com/google/common/collect/Lists .java # Lists.newArrayList% 28java.lang.Iterable% 29) संभावित रूप से 'लॉग (n)' बार आवंटित कर सकते हैं क्योंकि यह बैकिंग सरणी आवंटित करने से पहले आकार नहीं मिलता है। (हालांकि फिर से गुवा कोड को देखते हुए, मुझे बस एहसास हुआ कि मेरा अपना 'उदाहरण तब कास्ट' अनावश्यक है।) –

उत्तर

0

उत्कृष्ट जवाब और प्रदान की डॉक्स पढ़ने के बाद, मैं कुछ और कक्षाओं में चारों ओर poked और पाया क्या विजेता बनने लग रहा है, दोनों परीक्षण कोड के लिए और सीधे प्रश्न शीर्षक के लिए सरलता के संदर्भ में।

public Iterator<E> iterator() { 
    return new Itr(); 
} 

कहाँ Itr एक उच्च अनुकूलित किया है, Iterator<E> के कस्टमाइज़ किए गए क्रियान्वयन के साथ एक निजी भीतरी वर्ग है: जावा के मुख्य ArrayList कार्यान्वयन इस रत्न शामिल हैं। दुर्भाग्यवश, Iterator स्वचालित रूप से Iterable लागू नहीं करता है, इसलिए यदि मैं उस कोड पथ का परीक्षण करने के लिए अपने सहायक विधि में शर्मीली शूज़ करना चाहता हूं जो कि कलाकार नहीं है, तो मुझे इसे अपने स्वयं के जंक क्लास में लपेटना होगा जो Iterable लागू करता है (और Collection नहीं) और Itr देता है। यह पुनरावृत्ति कोड को लिखने के बिना आसानी से एक संग्रह को बदलने के लिए एक आसान तरीका है।

अंतिम नोट पर, कोड का मेरा अंतिम संस्करण स्वयं भी कलाकार नहीं करता है, क्योंकि अमरूद Lists.newArrayList प्रश्न में रनटाइम प्रकार का पता लगाने के साथ मैं काफी कुछ करता हूं। उदाहरण के लिए हाँ [ `ServiceLoader`] (https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html):

@GwtCompatible(serializable = true) 
public static <E> ArrayList<E> More ...newArrayList(Iterable<? extends E> elements) { 
    checkNotNull(elements); // for GWT 
    // Let ArrayList's sizing logic work, if possible 
    if (elements instanceof Collection) { 
    @SuppressWarnings("unchecked") 
    Collection<? extends E> collection = (Collection<? extends E>) elements; 
    return new ArrayList<E>(collection); 
    } else { 
    return newArrayList(elements.iterator()); 
    } 
} 
11

हालांकि कोई वर्ग है कि तुरंत आपकी आवश्यकताओं के अनुरूप और अपने परीक्षण कोड के पाठकों के लिए सहज ज्ञान युक्त किया जाएगा है, तो आप आसानी से अपने खुद गुमनाम वर्ग समझने में आसान है कि बना सकते हैं:

static Iterable<Integer> range(final int from, final int to) { 
    return new Iterable<Integer>() { 
     public Iterator<Integer> iterator() { 
      return new Iterator<Integer>() { 
       int current = from; 
       public boolean hasNext() { return current < to; } 
       public Integer next() { 
        if (!hasNext()) { throw new NoSuchElementException(); } 
        return current++; 
       } 
       public void remove() { /*Optional; not implemented.*/ } 
      }; 
     } 
    }; 
} 

Demo.

इस कार्यान्वयन गुमनाम है, और यह Collection<Integer> को लागू नहीं करता है। दूसरी ओर, यह पूर्णांक के एक गैर-खाली संख्यात्मक अनुक्रम उत्पन्न करता है, जिसे आप पूरी तरह से नियंत्रित कर सकते हैं।

10

प्रति शीर्षक के रूप में सवाल का जवाब करने के लिए:

वहाँ किसी भी जावा मानक वर्गों कि Collection को लागू करने के बिना Iterable को लागू कर रहे हैं?

यदि कभी किसी भी कक्षा कि Iterable<T> लागू कर सकते हैं कि यह भी Collection<T> को लागू नहीं करते:

पाठ से ?

उत्तर: https://docs.oracle.com/javase/8/docs/api/java/lang/class-use/Iterable.html

किसी भी अनुभाग कि Classes in XXX that implement Iterable कहते हैं, जावा इंटरफेस को लागू मानक वर्गों सूची जाएगा:

हाँ

निम्नलिखित जावाडोक पेज देखें। उनमें से कई Collection लागू नहीं करते हैं।

10

kludgy, हाँ, लेकिन मुझे लगता है कि कोड

Collection<Integer> _keys; 
if (keys instanceof Collection) { 
    _keys = (Collection<Integer>) keys; 
} else { 
    _keys = Lists.newArrayList(keys); 
} 

पूरी तरह से ध्वनि है। इंटरफेस Collection<T>Iterable<T> बढ़ाता है और आपको 2 अलग-अलग प्रकार के पैरामीटर के साथ एक ही इंटरफ़ेस को लागू करने की अनुमति नहीं है, इसलिए उदाहरण के लिए कोई वर्ग Collection<String> और Iterable<Integer> लागू नहीं कर सकता है।

कक्षा Integer अंतिम है, इसलिए Iterable<? extends Integer> और Iterable<Integer> के बीच का अंतर काफी हद तक अकादमिक है।

एक साथ लिया गया, अंतिम 2 पैराग्राफ साबित करते हैं कि अगर Iterable<? extends Integer> और Collection दोनों कुछ है, तो यह Collection<Integer> होना चाहिए। इसलिए आपका कोड सुरक्षित होने की गारंटी है। संकलक इस बात का यकीन है कि ताकि आप बयान से ऊपर

@SuppressWarnings("unchecked") 

लिख कर चेतावनी को दबाने नहीं कर सकते हो सकता है। कोड को सुरक्षित क्यों समझाया जाए, यह समझाने के लिए आपको एनोटेशन द्वारा टिप्पणी भी शामिल करनी चाहिए।

इस सवाल के लिए कि Iterable को लागू करने वाले कोई वर्ग हैं, लेकिन Collection नहीं है, क्योंकि अन्य ने उत्तर दिया है कि उत्तर हाँ है। हालांकि मुझे लगता है कि आप वास्तव में क्या पूछ रहे हैं कि क्या दो इंटरफेस रखने में कोई बात है या नहीं। कई अन्य ने यह पूछा है। एक विधि एक Collection तर्क है अक्सर जब (जैसे addAll() यह हो सकता है, और शायद, एक Iterable

संपादित किया जाना चाहिए

@Andreas टिप्पणी है कि Iterable केवल जावा 5 में पेश किया गया था में बताया गया है, जबकि Collection जावा 1.2 में पेश किया गया था, और सबसे मौजूदा तरीकों लेने एक Collection संगतता कारणों के लिए एक Iterable लेने के लिए retrofitted नहीं किया जा सका

+0

मुझे लगता है कि यह काम करने के लिए साबित हो सकता है, लेकिन ये सभी बुरे प्रथाएं हैं। विशेष रूप से, '@ SuppressWarnings' मानता है कि इस विधि को बनाए रखने वाले सभी भावी कोडर कभी गलती नहीं करेंगे। – VGR

+1

@VGR यही कारण है कि मैंने कहा कि यह बताने के लिए एक टिप्पणी शामिल है कि यह ठीक क्यों है। '@SuppressWarnings (" अनचेक ") का उपयोग पूरे जेडीके में किया जाता है, और यह स्थिति के लिए है जब आप साबित कर सकते हैं कि कुछ सुरक्षित है, लेकिन संकलक नहीं कर सकता - बिल्कुल यह स्थिति।अन्य बुरी प्रथाएं क्या हैं? –

+0

अनचेक कास्ट। तार्किक अर्थ में यह केवल "पूरी तरह से ध्वनि" है, और संभवतः यह तोड़ने वाली पहली बात होगी जब किसी और तरीके से कोड को संशोधित करने के लिए कोई और कदम उठाएगा। मैं यह कहने के लिए पसंद करूंगा "मुझे सीट बेल्ट की जरूरत नहीं है क्योंकि मैं एक अच्छा चालक हूं।" – VGR

5

कोर एपीआई में, केवल प्रकार है कि Iterable लेकिन हैं Collection नहीं। -

interface java.nio.file.Path 

interface java.nio.file.DirectoryStream 
interface java.nio.file.SecureDirectoryStream 

class java.util.ServiceLoader 

class java.sql.SQLException (and subclasses) 

तर्कसंगत रूप से ये सभी खराब डिज़ाइन हैं।

+1

मैं यह देखने में असफल रहा कि इनमें से कुछ "खराब डिज़ाइन" का प्रतिनिधित्व कैसे करते हैं, क्या आप आगे की व्याख्या कर सकते हैं? –

+1

@ ıɯɐƃoʇǝızuǝʞ - "तर्कसंगत" :) मेरे लिए, '(x: sqlEx)' खराब है क्योंकि इसका अर्थ तुरंत स्पष्ट नहीं है। इसे 'x (sqlEx.causes())' – ZhongYu

+0

'के रूप में डिज़ाइन करना बेहतर होगा, उन वर्गों की उपयोगिता को अलग करना, टेस्ट कोड के लिए नामित कुछ गैर-सहजता से उपयोग करना एक खराब डिज़ाइन होगा। विकल्पों की गणना के लिए +1। –

1

@bayou.io के जवाब में उल्लेख किया है, Iterable के लिए ऐसे ही एक कार्यान्वयन फाइल सिस्टम ट्रेवर्सल जावा 7.

में पेश आप जावा 8 पर, Iterable साथ retrofitted किया गया है हो सकता है के लिए हो तो के लिए नए Path वर्ग है (यानी करने के लिए अपने कार्यान्वयन नोट) है, जो मदद से आप StreamSupport साथ संयोजन के रूप में इसका इस्तेमाल एक default विधि) spliterator() (भुगतान ध्यान दिया:

public static <T> Collection<T> convert(Iterable<T> iterable) { 
    // using Collectors.toList() for illustration, 
    // there are other collectors available 
    return StreamSupport.stream(iterable.spliterator(), false) 
         .collect(Collectors.toList()); 
} 

यह वें पर आता है ई मामूली व्यय कि किसी भी तर्क जो पहले से ही Collection कार्यान्वयन एक अनावश्यक धारा-और-संग्रह ऑपरेशन के माध्यम से चला जाता है। यदि आप पहले से ही गुवा के CacheLoader का उपयोग कर रहे हैं, तो संभवत: आपके मूल कास्टिंग या अमरूद-आधारित विधियों की तुलना में मानकीकृत जेडीके-केवल दृष्टिकोण की संभावित प्रदर्शन हिट से अधिक होने की संभावना का उपयोग करना चाहिए, जो संभवतया म्यूट है।

इस बाहर का परीक्षण करने के विचार इस स्निपेट और नमूना उत्पादन:

// Snippet 
System.out.println(convert(Paths.get(System.getProperty("java.io.tmpdir")))); 
// Sample output on Windows 
[Users, MyUserName, AppData, Local, Temp] 
संबंधित मुद्दे