जावा सेफवर्ग्स एनोटेशन, क्या एक मानक या सर्वोत्तम अभ्यास मौजूद है?


175

मैं हाल ही में जावा @SafeVarargsएनोटेशन पर आया हूं । जावा असुरक्षित में एक परिवर्तनशील कार्य करने के लिए गुग्लिंग ने मुझे उलझा दिया (ढेर जहर; मिटाए गए प्रकार?), इसलिए मैं कुछ चीजें जानना चाहता हूं:

  1. क्या एक वैरिएबल जावा फंक्शन को असुरक्षित बनाता @SafeVarargsहै (अधिमानतः एक इन-डेप्थ उदाहरण के रूप में समझाया गया है)?

  2. यह एनोटेशन प्रोग्रामर के विवेक पर क्यों छोड़ा गया है? क्या यह ऐसा कुछ नहीं है जो संकलक को जांचने में सक्षम होना चाहिए?

  3. क्या कोई मानक है जिसका पालन करने के लिए यह सुनिश्चित करना चाहिए कि उसका कार्य वास्तव में सुरक्षित है? यदि नहीं, तो इसे सुनिश्चित करने के लिए सर्वोत्तम अभ्यास क्या हैं?


1
क्या आपने JavaDoc में उदाहरण (और स्पष्टीकरण) देखा है ?
जलोर्डो

2
आपके तीसरे प्रश्न के लिए, एक अभ्यास हमेशा एक पहला तत्व और अन्य होता है retType myMethod(Arg first, Arg... others):। यदि आप निर्दिष्ट नहीं करते हैं first, तो एक खाली सरणी की अनुमति है, और आपके पास बहुत अच्छी तरह से एक ही नाम हो सकता है जिसमें समान रिटर्न प्रकार होता है जिसमें कोई तर्क नहीं होता है, जिसका अर्थ है कि जेवीएम को यह निर्धारित करने में कठिन समय होगा कि किस विधि को बुलाया जाना चाहिए।
fge

4
@ ज्लॉर्डो मैंने किया था, लेकिन मुझे समझ में नहीं आता है कि इसका वेरिएग संदर्भ में क्यों दिया गया है क्योंकि कोई भी आसानी से इस स्थिति को एक वैरैगस फ़ंक्शन के बाहर हटा सकता है (यह सत्यापित किया गया है, अपेक्षित प्रकार की सुरक्षा चेतावनी और रनटाइम पर त्रुटियों के साथ संकलन)
ओरेन

जवाबों:


244

1) इंटरनेट पर और StackOverflow पर जेनेरिक और varargs के साथ विशेष मुद्दे के बारे में कई उदाहरण हैं। मूल रूप से, यह तब होता है जब आपके पास एक टाइप-पैरामीटर प्रकार के तर्कों की चर संख्या होती है:

<T> void foo(T... args);

जावा में, varargs एक संश्लिष्ट चीनी है जो संकलन-समय पर एक सरल "री-राइटिंग" से गुजरता है: प्रकार X...का एक varargs पैरामीटर एक प्रकार के पैरामीटर में परिवर्तित होता है X[]; और हर बार जब इस वैरगेज विधि के लिए कॉल किया जाता है, तो कंपाइलर "वैरिएबल तर्क" के सभी को इकट्ठा करता है, जो वैरैगस पैरामीटर में जाता है, और एक सरणी बनाता है जैसे new X[] { ...(arguments go here)... }

यह अच्छी तरह से काम करता है जब varargs प्रकार ठोस होता है String...। जब यह एक प्रकार का चर होता है T..., तो यह Tउस कॉल के लिए एक ठोस प्रकार ज्ञात होने पर भी काम करता है। जैसे यदि विधि के ऊपर एक वर्ग का हिस्सा थे Foo<T>, और आप एक है Foo<String>संदर्भ है, तो बुला fooपर यह क्योंकि हम जानते हैं उचित रहेगा Tहै Stringकोड में उस बिंदु पर।

हालांकि, यह तब काम नहीं करता है जब "मान" Tदूसरे प्रकार का पैरामीटर है। जावा में, प्रकार-पैरामीटर घटक प्रकार ( new T[] { ... }) की एक सरणी बनाना असंभव है । इसलिए जावा इसके बजाय उपयोग करता है new Object[] { ... }(यहां Objectऊपरी सीमा है T; यदि ऊपरी सीमा कुछ अलग थी, तो वह इसके बजाय होगी Object), और फिर आपको एक संकलक चेतावनी देता है।

तो क्या या बनाने के new Object[]बजाय क्या गलत है new T[]? खैर, जावा में सरणियाँ उनके घटक प्रकार को रनटाइम पर जानते हैं। इस प्रकार, पारित सरणी वस्तु में रनटाइम पर गलत घटक प्रकार होगा।

केवल तत्वों पर पुनरावृति करने के लिए, शायद सबसे अधिक उपयोग किया जाने वाला वेरिएग, यह कोई समस्या नहीं है (आप क्रम के प्रकार के बारे में परवाह नहीं करते हैं), इसलिए यह सुरक्षित है:

@SafeVarargs
final <T> void foo(T... args) {
    for (T x : args) {
        // do stuff with x
    }
}

हालांकि, किसी भी चीज के लिए जो रन किए गए घटक प्रकार के पारित सरणी पर निर्भर करता है, यह सुरक्षित नहीं होगा। यहाँ एक ऐसी चीज़ का सरल उदाहरण दिया गया है जो असुरक्षित और दुर्घटनाग्रस्त है:

class UnSafeVarargs
{
  static <T> T[] asArray(T... args) {
    return args;
  }

  static <T> T[] arrayOfTwo(T a, T b) {
    return asArray(a, b);
  }

  public static void main(String[] args) {
    String[] bar = arrayOfTwo("hi", "mom");
  }
}

यहां समस्या यह है कि हम इसे वापस करने के लिए किस प्रकार के argsहोने पर निर्भर हैं । लेकिन वास्तव में रनटाइम पर तर्क का प्रकार इसका उदाहरण नहीं है ।T[]T[]T[]

3) यदि आपकी विधि में तर्क का प्रकार है T...(जहाँ T किसी भी प्रकार का पैरामीटर है), तो:

  • सुरक्षित: यदि आपका तरीका केवल इस तथ्य पर निर्भर करता है कि सरणी के तत्व किस प्रकार के हैं T
  • असुरक्षित: यदि यह इस तथ्य पर निर्भर करता है कि सरणी किसका उदाहरण है T[]

सरणी के रनटाइम प्रकार पर निर्भर करने वाली चीजों में शामिल हैं: प्रकार के रूप में इसे वापस करना T[], इसे प्रकार के एक तर्क के रूप में पास करना T[], सरणी प्रकार का उपयोग करना .getClass(), इसे ऐसे तरीकों से पारित करना जो सरणी के रनटाइम प्रकार पर निर्भर करते हैं, जैसे List.toArray()और Arrays.copyOf(), आदि।

2) मैंने ऊपर उल्लेख किया भेद आसानी से स्वचालित रूप से प्रतिष्ठित होने के लिए बहुत जटिल है।


7
खैर अब यह सब समझ में आता है। धन्यवाद। तो बस यह देखने के लिए कि मैं आपको पूरी तरह से समझता हूं, हम कहते हैं कि हमारे पास एक वर्ग है जो कि FOO<T extends Something>एक varargs विधि के T [] को एक सरणी के रूप में वापस करना सुरक्षित होगा Somthing?
ओरेन

30
ध्यान देने योग्य हो सकता है कि एक मामला जहां यह (संभवतया) हमेशा उपयोग करने के लिए सही होता @SafeVarargsहै, जब आप सरणी के साथ एक ही चीज़ करते हैं, तो इसे किसी अन्य विधि से पास किया जाता है जो पहले से ही एनोटेट है (उदाहरण के लिए: मैं अक्सर खुद को वैरग तरीके लिखने के लिए पाता हूं अपने तर्क का उपयोग करके किसी सूची में परिवर्तित करने के Arrays.asList(...)लिए और किसी अन्य विधि से इसे पारित करने के लिए मौजूद हैं; इस तरह के मामले को हमेशा एनोटेट किया जा सकता है @SafeVarargsक्योंकि Arrays.asListइसमें एनोटेट है )।
जूल्स

3
@newacct मुझे नहीं पता कि जावा 7 में यह मामला था, लेकिन जावा 8 की अनुमति नहीं है @SafeVarargsजब तक कि विधि नहीं है staticया final, क्योंकि अन्यथा यह किसी असुरक्षित के साथ व्युत्पन्न वर्ग में ओवरराइड हो सकता है।
एंडी

3
और जावा 9 में इसे उन privateतरीकों के लिए भी अनुमति दी जाएगी जिन्हें ओवरराइड नहीं किया जा सकता है।
डेव एल

4

सर्वोत्तम प्रथाओं के लिए, इस पर विचार करें।

यदि आपके पास यह है:

public <T> void doSomething(A a, B b, T... manyTs) {
    // Your code here
}

इसे इसमें बदलें:

public <T> void doSomething(A a, B b, T... manyTs) {
    doSomething(a, b, Arrays.asList(manyTs));
}

private <T> void doSomething(A a, B b, List<T> manyTs) {
    // Your code here
}

मैंने पाया है कि मैं आमतौर पर अपने कॉलर्स के लिए अधिक सुविधाजनक बनाने के लिए केवल वैरगेज जोड़ता हूं। इसका उपयोग करने के लिए मेरे आंतरिक कार्यान्वयन के लिए लगभग हमेशा अधिक सुविधाजनक होगा List<>। इसलिए पिगी-बैक पर Arrays.asList()और यह सुनिश्चित करने के लिए कोई उपाय नहीं है कि मैं हीप प्रदूषण को लागू कर सकता हूं, यही मैं करता हूं।

मुझे पता है कि यह केवल आपके # 3 का जवाब देता है। newacct ने ऊपर # 1 और # 2 का शानदार जवाब दिया है, और मेरी इतनी प्रतिष्ठा नहीं है कि मैं इसे केवल एक टिप्पणी के रूप में छोड़ दूं। : पी

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.