क्या "वार्नर पैरामीटर के लिए टी का एक सामान्य सरणी" संकलक चेतावनी को हल करना संभव है?


153

यह प्रश्न में कोड का एक सरलीकृत संस्करण है, एक सामान्य वर्ग जेनेरिक प्रकार के मापदंडों के साथ एक और वर्ग का उपयोग करता है और वैरिएग मापदंडों के साथ सामान्य प्रकार में से एक को पारित करने की आवश्यकता है:

class Assembler<X, Y> {
    void assemble(X container, Y... args) { ... }
}

class Component<T> {
    void useAssembler(T something) {

        Assembler<String, T> assembler = new Assembler<String, T>();

        //generates warning:
        // Type safety : A generic array of T is
        // created for a varargs parameter
        assembler.assemble("hello", something);
    }

}

क्या इस चेतावनी का सामना किए बिना वैरिकाज़ पद्धति के सामान्य पैरामीटर के साथ गुजरने का कोई सही तरीका है?

जरूर कुछ पसंद है

assembler.assemble("hello", new T[] { something });

काम नहीं करता क्योंकि आप जेनेरिक सरणियाँ नहीं बना सकते।


3
एक अजीब। ऐसा लगता है कि कंपाइलर को यहां पूर्ण प्रकार की सुरक्षा का आश्वासन देने में सक्षम होना चाहिए।
एरिकसन

3
एंजेलिका लैंगर के जावा जेनरिक एफएक्यू में संबंधित प्रविष्टि: angelikalanger.com/GenericsFAQ/FAQSections/…
फ्लो

जवाबों:


88

जोड़ने के अलावा @SuppressWarnings("unchecked"), मुझे ऐसा नहीं लगता।

इस बग रिपोर्ट में अधिक जानकारी है, लेकिन यह सामान्य प्रकार के सरणियों को पसंद नहीं करने वाले कंपाइलर को उबालता है।


3
उल्लेख करना भूल गया कि मैं @SuppressWarnings ("अनियंत्रित") से बचना चाहता था। उस बग रिपोर्ट से मुझे थोड़ी उम्मीद है!
मैट बी

3
जैसा कि यहोशू बलोच ने इसे प्रभावी जावा में रखा: "जेनरिक और सरणियों को न मिलाएं।"
टिममोस

20
उसके बाद, स्पष्ट रूप से: जेनरिक के साथ वर्जर्स को उपयोगकर्ता न करें! सही ... वेरिएग टू अर्रे और नॉट कलेक्शन का निर्णय हमेशा स्टिंग जावा को बनाए रखेगा। अच्छी तरह से किया श्री गोस्लिंग।
बर्नस्टीन

57

टॉम हॉन्टिन ने एक टिप्पणी में इस पर ध्यान दिलाया, लेकिन अधिक स्पष्ट होने के लिए: हाँ, आप इसे घोषणा-स्थल पर हल कर सकते हैं (बजाय संभावित कई) कॉल साइटों को: JDK7 पर स्विच करें।

जैसा कि आप जोसेफ डार्सी के ब्लॉग पोस्ट में देख सकते हैं , जावा 7 के लिए कुछ छोटे वृद्धिशील भाषा में सुधार के लिए प्रोजेक्ट कॉइन अभ्यास ने बॉब ली के प्रस्ताव को कुछ इस तरह से स्वीकार करने की स्वीकृति दी@SuppressWarnings("varargs") जो इस चेतावनी को उन स्थितियों में दूर जहां यह जाना जाता था। सुरक्षित।

यह इस प्रतिबद्धता के साथ OpenJDK में लागू किया गया है

यह आपकी परियोजना के लिए उपयोगी हो सकता है या नहीं भी हो सकता है (बहुत से लोग JVM के पूर्व-रिलीज़ अस्थिर संस्करण पर स्विच करने में खुश नहीं होंगे!) लेकिन शायद यह है - या शायद कोई है जो इस प्रश्न को बाद में पाता है (JDK7 के बाद बाहर है) ) यह उपयोगी मिलेगा।


7
उल्लिखित प्रोजेक्ट कॉइन सुविधा अब उपलब्ध है - देखें जावा में @SafeVargs 7.
जॉर्ज हॉकिंस

बॉब के प्रस्ताव में वैकल्पिक ई मोहक है।
क्रिस्टोफर पेरी

Java 8 @SuppressWarnings ("varargs") के बजाय @SafeVarargs का उपयोग करता प्रतीत होता है
पॉल विंट्ज़

17

यदि आप एक धाराप्रवाह प्रकार के इंटरफेस के बाद हैं, तो आप बिल्डर पैटर्न की कोशिश कर सकते हैं। भिन्न के रूप में संक्षिप्त नहीं है लेकिन यह सुरक्षित है।

एक स्थैतिक रूप से टाइप की गई विधि, बिल्डर का उपयोग करते समय बायलरप्लेट में से कुछ को समाप्त कर सकती है, जबकि प्रकार की सुरक्षा को बनाए रखती है।

बनाने वाला

public class ArgBuilder<T> implements Iterable<T> {

    private final List<T> args = new ArrayList<T>();

    public ArgBuilder<T> and(T arg) {
        args.add(arg);
        return this;
    }

    @Override
    public Iterator<T> iterator() {
        return args.iterator();
    }

    public static <T> ArgBuilder<T> with(T firstArgument) {
        return new ArgBuilder<T>().and(firstArgument);
    }
}

उसका इस्तेमाल कर रहे हैं

import static com.example.ArgBuilder.*;

public class VarargsTest {

    public static void main(String[] args) {
        doSomething(new ArgBuilder<String>().and("foo").and("bar").and("baz"));
        // or
        doSomething(with("foo").and("bar").and("baz"));
    }

    static void doSomething(Iterable<String> args) {
        for (String arg : args) {
            System.out.println(arg);
        }
    }
}

1
रचना की शक्ति। मुझे यह वार्गर्स से बहुत अधिक पसंद है, यह अधिक अभिव्यंजक है।
क्रिस्टोफर पेरी

1
@ChristopherPerry अच्छी तरह से आप अपने कोड आधार पर भी विचार करना होगा। अंतर्निहित Collection(इस मामले में ArrayList) कॉलर पर मजबूर किया जाता है, जबकि उन्हें पता चल सकता है कि एक LinkedListअधिक उपयुक्त है, या एक अपरिवर्तनीय सरणी ही है (जैसे ओपी प्रश्न से भिन्न रूप)। एक विशेषीकृत उपयोग के मामले में यह उचित हो सकता है, लेकिन केवल यह इंगित करता है कि यह एक सीमा है, एक तरह से यह आपके और आपकी आवश्यकताओं के आसपास के कोड पर निर्भर करता है।
searchengine27

5

वार्क विधि आह्वान में वस्तु को स्पष्ट रूप से कास्टिंग करने वाले पैरामीटर @SuppressWarnings का सहारा लिए बिना कंपाइलर को खुश करेंगे।

public static <T> List<T> list( final T... items )
{
    return Arrays.asList( items );
}

// This will produce a warning.
list( "1", 2, new BigDecimal( "3.5" ) )

// This will not produce a warning.
list( (Object) "1", (Object) 2, (Object) new BigDecimal( "3.5" ) )

// This will not produce a warning either. Casting just the first parameter to 
// Object appears to be sufficient.
list( (Object) "1", 2, new BigDecimal( "3.5" ) )

मेरा मानना ​​है कि यहाँ समस्या यह है कि संकलक को यह पता लगाने की जरूरत है कि किस प्रकार के सरणी को बनाना है। यदि विधि सामान्य नहीं है, तो संकलक विधि से टाइप जानकारी का उपयोग कर सकता है। यदि विधि सामान्य है, तो यह आह्वान पर उपयोग किए जाने वाले मापदंडों के आधार पर सरणी प्रकार का पता लगाने की कोशिश करती है। यदि पैरामीटर प्रकार होमोजेनिक हैं, तो वह कार्य आसान है। यदि वे अलग-अलग होते हैं, तो संकलक मेरी राय में बहुत चालाक होने की कोशिश करता है और एक यूनियन-प्रकार के सामान्य सरणी बनाता है। तब यह आपको इसके बारे में चेतावनी देने के लिए मजबूर महसूस करता है। एक सरल समाधान ऑब्जेक्ट बनाने के लिए होता [] जब प्रकार को बेहतर ढंग से संकुचित नहीं किया जा सकता है। उपर्युक्त समाधान बस यही कहता है।

इसे बेहतर ढंग से समझने के लिए, निम्न सूची 2 विधि की तुलना में उपरोक्त सूची विधि में इनवोकेशन के साथ खेलें।

public static List<Object> list2( final Object... items )
{
    return Arrays.asList( items );
}

यह काम करता है, उदाहरण के लिए: Iterator <?> It = Arrays.asList ((Object) t) .iterator; if (if, hasNext ()) {class = it.next ()। getClass (); } उदाहरण के लिए अज्ञात प्रकार की एक सरणी से किसी वस्तु का वर्ग प्राप्त करने के लिए।
ggb667

2

आप जावा 7 से मेथड में @SafeVarargs जोड़ सकते हैं , और आपको क्लाइंट कोड पर एनोटेट नहीं करना पड़ेगा।

class Assembler<X, Y> {

    @SafeVarargs
    final void assemble(X container, Y... args) {
        //has to be final...
    }
}

1

आपके पास तरीके ओवरलोड हो सकते हैं। यह आपकी समस्या को हल नहीं करता है लेकिन यह चेतावनी की संख्या को कम करता है (और हाँ, यह एक हैक है!)

class Assembler<X, Y> {
  void assemble(X container, Y a1) { ... }
  void assemble(X container, Y a1, Y a2) { ... }
  void assemble(X container, Y a1, Y a2, Y a3) { ... }
  void assemble(X container, Y a1, Y a2, Y a3, Y a4) { ... }
  void assemble(X container, Y... args) { ... }
}

23
Ew। यह ठीक उसी तरह की हैक है जिसे वैरगेज को रोकने के लिए माना जाता है।
अमांडा एस

1
यह एक मान्य दृष्टिकोण हो सकता है, उदाहरण के लिए, अमरूद के इम्युनटेबलसेट पर एक नज़र डालें ।
जोनाथन

1

इसे हल करना एक बहुत ही आसान समस्या है: उपयोग करें List<T>!

संदर्भ प्रकार की सरणियों से बचा जाना चाहिए।

जावा (1.7) के वर्तमान संस्करण में, आप विधि को चिह्नित कर सकते हैं @SafeVargsजिसके साथ कॉलर से चेतावनी को हटा दिया जाएगा। उस के साथ सावधान, और आप अभी भी विरासत सरणियों के बिना बेहतर कर रहे हैं।

वर्गर्स मेथड तकनीक नोट के साथ गैर-रिफ़र्जेबल फॉर्मल पैरामीटर्स का उपयोग करते समय बेहतर कंपाइलर चेतावनी और त्रुटियां भी देखें ।


6
यह एक varargs पैरामीटर के साथ अपरिहार्य है, है ना?
मैट बी

4
JDK7 के लिए एक प्रस्ताव है कि चेतावनी दमन को इसके उपयोग के बजाय वार्गर्स विधि घोषणा पर जाने की अनुमति दी जाए।
टॉम हॉल्टिन - 15

11
यह लेखक के सवाल के एक महत्वपूर्ण पहलू को पूरी तरह से नजरअंदाज करता है - varargs पैरामीटर DO एक सरणी बनाते हैं, और जो इस चेतावनी को उत्पन्न करता है।
डैनियल यांकोव्स्की

2
मैं @Tom हॉकिन से सहमत हूं - डीललाइन। विवरणों के लिए देखें बलोच << एफेक्टिव जावा >> आइटम 25: एरे को सूचियों को वरीयता दें।
स्टेन कुरिलिन 29'10

2
मैं आम तौर पर इस पर बलोच से सहमत हूं, लेकिन varargs नियम का एक स्पष्ट अपवाद है ...
जोएरी हेंड्रिकक्स

0

जब सामान्य प्रकार के सरणियों के साथ काम करते हैं, तो मुझे सामान्य प्रकार का संदर्भ देने के लिए मजबूर किया जाता है। इसके साथ, मैं वास्तव में java.lang.reflect.Array का उपयोग करके सामान्य कोड कर सकता हूं।

http://java.sun.com/javase/6/docs/api/java/lang/reflect/Array.html


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