2016-02-20 8 views
11

मैं अपने कोड में एक परिदृश्य में जहाँ मैं एक वर्ग की तरह दो अलग-अलग प्रकार के लिए एक इंटरफ़ेस को लागू करने, इस उदाहरण की तरह होता है:कोटलिन में एक ही सामान्य इंटरफ़ेस से दो बार (अलग-अलग प्रकार के) से प्राप्त करने का कोई तरीका?

interface Speaker<T> { 
    fun talk(value: T) 
} 

class Multilinguist : Speaker<String>, Speaker<Float> { 
    override fun talk(value: String) { 
     println("greetings") 
    } 

    override fun talk(value: Float) { 
     // Do something fun like transmit it along a serial port 
    } 
} 

Kotlin इससे खुश नहीं है, का हवाला देते हुए:

Type parameter T of 'Speaker' has inconsistent values: kotlin.String, kotlin.Float 
A supertype appears twice 

मुझे पता है कि एक संभावित समाधान निम्नलिखित कोड को लागू करना है, जहां मैं इंटरफेस को <Any> के साथ कार्यान्वित करता हूं और फिर अपने आप प्रकारों की जांच करता हूं और उन्हें अपने कार्यों में भेजता हूं।

interface Speaker<T> { 
    fun talk(value: T) 
} 

class Multilinguist : Speaker<Any> { 
    override fun talk(value: Any) { 
     when (value) { 
      is String -> 
       internalTalk(value) 
      is Float -> 
       internalTalk(value) 
     } 
    } 

    fun internalTalk(value: String) { 
     println(value) 
    } 

    fun internalTalk(value: Float) { 
     // Do something fun like transmit it along a serial port 
    } 
} 

हालांकि, का मानना ​​है कि जैसे मैं प्रकार सुरक्षा और क्या वर्ग के लिए प्रयोग किया जाता है के बारे में संचार को हटाने कर रहा हूँ, और रेखा नीचे मुसीबत के लिए पूछ रहा है। Kotlin में इसे लागू करने के लिए एक बेहतर तरीका है? इसके अतिरिक्त - इसके पीछे तर्क क्या है जिस तरह से मैंने पहले नमूने में संकेत दिया था? इंटरफेस केवल हस्ताक्षरों का एक अनुबंध नहीं है जो मुझे लागू करने की आवश्यकता है, या क्या यहां कुछ ऐसा है जो मुझे जेनेरिक शामिल है?

उत्तर

14

हां, आप जेवीएम पर जेनिक्स कार्यान्वयन का एक महत्वपूर्ण विवरण खो रहे हैं: the type erasure। संक्षेप में, कक्षाओं के संकलित बाइटकोड में वास्तव में जेनेरिक प्रकारों के बारे में कोई जानकारी नहीं होती है (इस तथ्य के बारे में कुछ मेटाडेटा को छोड़कर कि एक वर्ग या विधि सामान्य है)। सभी प्रकार की जांच संकलन समय पर होती है, और इसके बाद कोड में कोई सामान्य प्रकार नहीं रहता है, केवल Object है।

अपने मामले में समस्या की खोज करने के लिए, (, आईडिया में Tools -> Kotlin -> Show Kotlin Bytecode, या किसी अन्य उपकरण) बाईटकोड पर सिर्फ देखो। Converter की बाईटकोड सामान्य प्रकार मिटाए जाने में

interface Converter<T> { 
    fun convert(t: T): T 
} 

class Reverser(): Converter<String> { 
    override fun convert(t: String) = t.reversed() 
} 

:

// access flags 0x401 
// signature (TT;)TT; 
// declaration: T convert(T) 
public abstract convert(Ljava/lang/Object;)Ljava/lang/Object; 

और यहाँ तरीकों Reverser की बाईटकोड में पाए जाते हैं:

// access flags 0x1 
public convert(Ljava/lang/String;)Ljava/lang/String; 
    ... 

// access flags 0x1041 
public synthetic bridge convert(Ljava/lang/Object;)Ljava/lang/Object; 
    ... 
    INVOKEVIRTUAL Reverser.convert (Ljava/lang/String;)Ljava/lang/String; 
    ... 

की इस सरल उदाहरण पर विचार करें Converter इंटरफ़ेस का उत्तराधिकारी, Reverser को विधि के साथ विधि के साथ होना चाहिए एक ही हस्ताक्षर, यानी, एक प्रकार मिटा दिया गया है। यदि वास्तविक कार्यान्वयन विधि में अलग हस्ताक्षर हैं, तो bridge method जोड़ा गया है। यहां हम देखते हैं कि बाइटकोड में दूसरी विधि बिल्कुल पुल विधि है (और यह पहली बार कॉल करती है)।

तो, कई सामान्य इंटरफ़ेस कार्यान्वयन एक दूसरे के साथ मामूली रूप से संघर्ष करेंगे, क्योंकि एक निश्चित हस्ताक्षर के लिए केवल एक पुल विधि हो सकती है।

इसके अलावा, यदि यह संभव था, न तो जावा और न ही कोटलिन has method overloading based on return value type, और कभी-कभी तर्कों में अस्पष्टता भी होगी, इसलिए एकाधिक विरासत काफी सीमित होगी।

हालाँकि, Project Valhalla के साथ बदल जाएगा (संशोधित जेनरिक रनटाइम पर वास्तविक प्रकार बनाए रखेंगे), लेकिन फिर भी मैं एकाधिक सामान्य इंटरफ़ेस विरासत की अपेक्षा नहीं करता।

संबंधित मुद्दे

 संबंधित मुद्दे