2013-01-12 31 views
6

मैं कुछ जावा कोड का अनुवाद करने की कोशिश कर रहा हूं जो वाइल्डकार्ड जेनेरिक का उपयोग सी # में करता है। मेरी समस्या यह है कि जावा एक वाइल्डकार्ड के साथ इस्तेमाल होने पर एक सामान्य प्रकार को कॉन्वर्सेंट और contravariant होने की अनुमति देता है।जावा बाउंड वाइल्डकार्ड (IInterf <?>) के लिए .NET समकक्ष?

जावा [यह एक पिछले question से स्पिन-ऑफ घिरा-वाइल्डकार्ड का एक सरल मामले से निपटने है] - काम करता है:

class Impl { } 

interface IGeneric1<T extends Impl> { 
    void method1(IGeneric2<?> val); 
    T method1WithParam(T val); 
} 

interface IGeneric2<T extends Impl> { 
    void method2(IGeneric1<?> val); 
} 

abstract class Generic2<T extends Impl> implements IGeneric2<T> { 

    // !! field using wildcard 
    protected IGeneric1<?> elem; 

    public void method2(IGeneric1<?> val1) { 
     val1.method1(this); 

     //assignment from wildcard to wildcard 
     elem = val1; 
    } 
} 

abstract class Generic<T extends Impl> implements IGeneric1<T>, IGeneric2<T> { 

    public void method1(IGeneric2<?> val2) { 
     val2.method2(this); 
    } 
} 

# सी - संकलन नहीं है ...

class Impl { } 

interface IGeneric1<T> where T:Impl { 
    //in Java: 
    //void method1(IGeneric2<?> val); 
    void method1<U>(IGeneric2<U> val) where U : Impl; //see this Q for 'why' 
           // https://stackoverflow.com/a/14277742/11545 

    T method1WithParam(T to); 
} 

interface IGeneric2<T>where T:Impl { 
    void method2<U>(IGeneric1<U> val) where U : Impl; 
} 

abstract class Generic2<T, TU>: IGeneric2<T> //added new type TU 
    where T : Impl 
    where TU : Impl 
{ 
    //in Java: 
    //protected IGeneric1<?> elem; 
    protected IGeneric1<TU> elem; 

    //in Java: 
    //public void method2(IGeneric1<?> val1) 
    public void method2<U>(IGeneric1<U> val) 
     where U : TU //using TU as constraint 
    { 
     elem = val; //Cannot convert source type 'IGeneric1<U>' 
        //to target type 'IGeneric1<TU>' 
    } 
    public abstract void method1WithParam(T to); 
} 

abstract class Generic<T> : IGeneric1<T>, IGeneric2<T> where T : Impl 
{ 
    //in Java: 
    //public void method1(IGeneric2<?> val2) 
    public void method1<U>(IGeneric2<U> val2) where U : Impl 
    { 
     val2.method2(this); 
    } 

    public abstract T method1WithParam(T to); 
    public abstract void method2<U>(IGeneric1<U> val) where U : Impl; 
    public abstract void nonGenericMethod(); 
} 

यदि मैं interface IGeneric1<T> सेबदलता हूंउपरोक्त त्रुटि दूर चला जाता है, लेकिन method1WithParam(T) विचरण के बारे में शिकायत:

Parameter must be input-safe. Invalid variance: The type parameter 'T' must be 
contravariantly valid on 'IGeneric1<out T>'. 
+0

मुझे जावा जेनिक्स बहुत कुछ नहीं पता है। लेकिन क्या जावा कोड टाइप-सुरक्षित है? – Euphoric

+1

क्या आप कृपया प्रदान कर सकते हैं कि जावा कोड कैसा होगा या कहलाया जाना चाहिए? मुझे अभी भी समझना मुश्किल लगता है कि कोई ऐसा राक्षस क्यों बनायेगा। – Euphoric

+0

ध्यान दें कि सादगी के लिए सी # की भिन्नता बाधा जानबूझकर अधिक प्रतिबंधक हैं। यह पूरी तरह से संभव है कि आप जावा कोड में व्यक्त कर रहे हैं, बस सी # में सीधा समकक्ष नहीं है। – millimoose

उत्तर

3

मुझे यह कहकर शुरू करना है कि निश्चित रूप से एक डिजाइन समीक्षा की तरह दिखना शुरू हो रहा है। मूल जावा क्लास IGeneric1<?> सदस्य को जोड़ती है, लेकिन इसके प्रकार के तर्क को जानने के बिना method1WithParam को एक प्रकार-सुरक्षित तरीके से कॉल करने की कोई संभावना नहीं है।

इसका मतलब है कि elem का उपयोग केवल method1 सदस्य को कॉल करने के लिए किया जा सकता है, जिसका हस्ताक्षर IGeneric1 के प्रकार पैरामीटर पर निर्भर नहीं है। यह इस प्रकार है कि method1 एक गैर सामान्य इंटरफ़ेस में विभाजित किया जा सकता है:

// C# code: 
interface INotGeneric1 { 
    void method1<T>(IGeneric2<T> val) where T : Impl; 
} 

interface IGeneric1<T> : INotGeneric1 where T : Impl { 
    T method1WithParam(T to); 
} 

इस के बाद, class Generic2 एक INotGeneric1 सदस्य बजाय समेकित कर सकते हैं:

abstract class Generic2<T>: IGeneric2<T> where T : Impl 
{ 
    protected INotGeneric1 elem; 

    // It's highly likely that you would want to change the type of val 
    // to INotGeneric1 as well, there's no obvious reason to require an 
    // IGeneric1<U> 
    public void method2<U>(IGeneric1<U> val) where U : Impl 
    { 
     elem = val; // this is now OK 
    } 
} 
बेशक

अब आप elem.method1WithParam कॉल नहीं कर सकते जब तक कि आप का सहारा कोस्ट या प्रतिबिंबित करने के लिए, भले ही यह ज्ञात हो कि ऐसी विधि मौजूद है और यह किसी अज्ञात प्रकार X के साथ एक तर्क तर्क के रूप में सामान्य है। हालांकि, जावा कोड के समान ही प्रतिबंध है; यह सिर्फ इतना है कि सी # कंपाइलर इस कोड को स्वीकार नहीं करेगा, जबकि जावा केवल शिकायत करेगा यदि आप method1WithParam1 पर कॉल करने का प्रयास करते हैं।

+0

मेरे संदेह की पुष्टि करने के लिए धन्यवाद। मैंने विशेष रूप से एक प्रश्न लिखा था कि मैं उस क्षेत्र पर सामान्य तरीकों को कैसे कॉल कर सकता हूं; मैंने सोचा कि मुझे कुछ याद आ रहा था: http://stackoverflow.com/q/14295032/11545। जावा कोडेबेस में अपराधी इंटरफेस एक सार्वजनिक है, इसलिए मैंने सोचा कि अगर मैं सी # में 1: 1 पत्राचार नहीं पा रहा हूं तो मैं कुछ संभावित बाहरी उपयोग-मामले तोड़ सकता हूं। बाहर निकलता है ऐसे कोई संभावित उपयोग-मामले नहीं हैं। –

2

जावा एक प्रकार दोनों संस्करण और covariant होने की अनुमति नहीं है। आपके पास यह तथ्य है कि आप IGeneric1<?> elem कक्षा Generic2 में घोषित करते हुए एक भ्रम है कि आप इसकी विधि T method1WithParam(T val); का उपयोग नहीं करते हैं; इसलिए जावा को इस घोषणा के साथ कोई समस्या नहीं दिख रही है। हालांकि, आप elem के माध्यम से इसका उपयोग करने की कोशिश करेंगे, हालांकि यह एक त्रुटि को ध्वजांकित करेगा।

इसे चित्रित करने के लिए, निम्नलिखित test() को Generic2 कक्षा में जोड़ें जो elem.method1WithParam() फ़ंक्शन को कॉल करने का प्रयास करेगा, लेकिन इससे एक कंपिलेटर त्रुटि होती है। आक्रामक लाइन बाहर टिप्पणी की गई है, ताकि आप क्रम में इसे फिर से स्थापित करने के लिए त्रुटि पुन: पेश करने की जरूरत है:

abstract class Generic2<T extends Impl> implements IGeneric2<T> { 

    // !! field using wildcard 
    protected IGeneric1<?> elem; 

    public void method2(IGeneric1<?> val1) { 
     val1.method1(this); 

     //assignment from wildcard to wildcard 
     elem = val1; 
    } 

    public void test() { 
     Impl i = new Impl(); 

       // The following line will generate a compiler error: 
     // Impl i2 = elem.method1WithParam(i); // Error! 
    } 
} 

जावा से यह त्रुटि साबित होता है कि हम दोनों covariant और contravariant के रूप में एक सामान्य प्रकार का उपयोग नहीं कर सकते हैं और इस; भले ही कुछ घोषणा विपरीत साबित होती है। सी # कंपाइलर के साथ, आपके पास संकलन त्रुटि प्राप्त करने से पहले उस बंद होने का मौका भी नहीं है: यदि आप इंटरफेस IGeneric1<T extends Impl> को IGeneric1<out T extends Impl> के साथ भिन्नता घोषित करने का प्रयास करते हैं; T method1WithoutParam();

दूसरा, मैंने संदर्भ .NET equivalent for Java wildcard generics <?> with co- and contra- variance? पर एक नज़र डाली लेकिन मुझे यह स्वीकार करना होगा कि मुझे समझ में नहीं आता कि इसे समाधान के रूप में क्यों देखा जा सकता है। <T extends Impl> जैसे प्रतिबंधों का प्रकार असंबद्ध वाइल्डकार्ड पैरामीटरयुक्त प्रकार (<?>) या भिन्नता (<? extends Impl>) से कोई लेना देना नहीं है और मुझे नहीं लगता कि पहले के साथ सेकंड को प्रतिस्थापित करने के लिए सामान्य समाधान के रूप में देखा जा सकता है। हालांकि, कुछ मौकों पर, यदि आपको वास्तव में वाइल्डकार्ड पैरामीटरयुक्त प्रकार (<?>) या हाँ से भिन्न प्रकार का उपयोग करने की आवश्यकता नहीं है, तो आप यह रूपांतरण कर सकते हैं। हालांकि, अगर आप वास्तव में अपने जावा कोड में उनका उपयोग नहीं करते हैं, तो इसे भी ठीक किया जाना चाहिए।

जावा जेनरिक के साथ, आप बहुत अधिक अपर्याप्तता पेश कर सकते हैं लेकिन आपको सी # कंपाइलर के साथ यह मौका नहीं मिलेगा। यह विशेष रूप से सच है कि सी # में, कक्षाओं और structs पूरी तरह से reifiable हैं और इसलिए, भिन्नता (दोनों covariance और contravariance) का समर्थन नहीं करते हैं। आप केवल एक इंटरफ़ेस की घोषणा और प्रतिनिधियों के लिए इसका उपयोग कर सकते हैं; अगर मुझे ठीक से याद है।

अंत में, जब बहुरूपता शामिल होती है, अक्सर अनावश्यक जेनेरिक प्रकारों का उपयोग करने की एक खराब प्रवृत्ति होती है; वाइल्डकार्ड पैरामीटरयुक्त प्रकारों और भिन्नता के साथ या बिना। यह अक्सर एक लंबा और जटिल कोड का कारण बनता है; पढ़ने और उपयोग करने के लिए मुश्किल और लिखने के लिए भी कठिन है।मैं आपको इस जावा कोड को देखने के लिए दृढ़ता से सुझाव दूंगा और देख सकता हूं कि यह केवल इतना ही आवश्यक है कि केवल पॉलीमोर्फिज्म के साथ एक बहुत ही सरल कोड के बजाय या जेनेरिक के साथ बहुरूपता के संयोजन के बिना, लेकिन भिन्नता या वाइल्डकार्ड पैरामीटरयुक्त प्रकार के बिना।

+0

हां, मैंने भी कोशिश की - लेकिन सोचा कि जेनेरिक तरीकों का उपयोग करने का एक चालाक तरीका हो सकता है, जो कि मैं गायब था। मैंने इसके बारे में http://stackoverflow.com/q/14295032/11545 पर पूछा लेकिन मुझे लगता है कि आपका जवाब और जॉन स्पष्ट है। –

+0

आपके अंतिम पैराग्राफ के बारे में - मैं पूरी तरह से सहमत हूं।मैंने बस इतना पता लगाने की कोशिश की कि कोड क्या करता है और इसे सरल कैसे करें (यह इस उदाहरण से बहुत अधिक उलझन में है) इस बिंदु पर कि मैं किसी और को समस्याग्रस्त बिट्स समझा सकता हूं, और इसे स्वयं प्रक्रिया। ओटीओएच, मुझे अतीत में इसी तरह की जटिलताओं के दोषी होने के लिए स्वीकार करना होगा; यह एक फिसलन ढलान है। –