2015-06-17 33 views
10

में संपत्ति संपत्ति प्रतिनिधि का उपयोग करें कोटलिन ने संपत्तियों का प्रतिनिधित्व किया है जो एक बहुत अच्छी सुविधा है। लेकिन कभी-कभी get() और set() विधियां पर्याप्त नहीं हैं। मान लीजिए कि मैं Closeable ऑब्जेक्ट को आलसी बनाना चाहता हूं और इसे बाद में बंद करना चाहता हूं। यहाँ कैसे इस तरह के प्रतिनिधि संपत्ति लागू किया जा सकता का एक उदाहरण है:कोटलिन

fun <T : Closeable> closeableLazy(initializer:() -> T) = 
     CloseableLazyVal(initializer) 

class CloseableLazyVal<T : Closeable>(
    private val initializer:() -> T 
) : ReadOnlyProperty<Any?, T> { 

    private var value: T? = null 

    override fun get(thisRef: Any?, desc: PropertyMetadata): T { 
     if (value == null) { 
      value = initializer() 
     } 
     return value 
    } 

    fun close() { 
     value?.close() 
    } 
} 

और वह मैं इसे कैसे उपयोग करना चाहते हैं क्या है:

private val stream by closeableLazy { FileOutputStream("/path/to/file") } 

fun writeBytes(bytes: ByteArray) { 
    stream.write(bytes) 
} 

override fun close() { 
    stream::delegate.close() // This line will not compile 
} 

दुर्भाग्य से, इस दृष्टिकोण काम नहीं करता है क्योंकि ऐसा लगता है कि Kotlin नहीं करता है संपत्ति प्रतिनिधियों तक सीधे पहुंचने की अनुमति नहीं है। क्या मैं चाहता हूं कि ऐसा करने का कोई तरीका है? या क्या कोटलिन को ऐसी कार्यक्षमता जोड़ने की कोई योजना है क्योंकि यह एक साफ सुविधा होगी।

+1

वैसे, आप यह बताने के लिए कॉल की जरूरत नहीं है। बस मूल्य का उपयोग करें? .close() –

+0

@cypressious, आप सही हैं, धन्यवाद। – Michael

उत्तर

6

ठीक है, तो मैं निम्नलिखित समाधान के साथ आया था:

fun <T : Closeable> closeableLazy(initializer:() -> T) = 
     CloseableLazyVal(initializer) 

class CloseableLazyVal<T : Closeable>(
     private val initializer:() -> T 
) : ReadOnlyProperty<CloseableDelegateHost, T> { 

    private var value: T? = null 

    override fun get(thisRef: CloseableDelegateHost, desc: PropertyMetadata): T { 
     if (value == null) { 
      value = initializer() 
      thisRef.registerCloseable(value!!) 
     } 
     return value!! 
    } 

} 

interface CloseableDelegateHost : Closeable { 
    fun registerCloseable(prop : Closeable) 
} 

class ClosableDelegateHostImpl : CloseableDelegateHost { 

    val closeables = arrayListOf<Closeable>() 

    override fun registerCloseable(prop: Closeable) { 
     closeables.add(prop) 
    } 

    override fun close() = closeables.forEach { it.close() } 
} 

class Foo : CloseableDelegateHost by ClosableDelegateHostImpl() { 
    private val stream by closeableLazy { FileOutputStream("/path/to/file") } 

    fun writeBytes(bytes: ByteArray) { 
     stream.write(bytes) 
    } 

} 

ध्यान दें, कि संपत्ति के प्राप्त विधि एक पैरामीटर thisRef है। मुझे इसकी आवश्यकता है कि यह CloseableDelegateHost से प्राप्त हो जो बंद होने पर पंजीकृत किसी भी पंजीकृत Closeable को बंद कर देगा। कार्यान्वयन को सरल बनाने के लिए मैं इस इंटरफेस को एक साधारण सूची-आधारित कार्यान्वयन में प्रस्तुत करता हूं।

अद्यतन (टिप्पणियों से नकल): मुझे एहसास हुआ, तो आप सिर्फ एक अलग संपत्ति के रूप में प्रतिनिधि की घोषणा और फिर इसे करने के लिए दूसरी संपत्ति को सौंपने कर सकते हैं। इस तरह आप प्रतिनिधि को आसानी से एक्सेस कर सकते हैं।

private val streamDelegate = closeableLazy { FileOutputStream("/path/to/file") } 
private val stream by streamDelegate 

fun writeBytes(bytes: ByteArray) { 
    stream.write(bytes) 
} 

override fun close() { 
    streamDelegate.close() 
} 
+1

उत्तर के लिए धन्यवाद। आपका समाधान अच्छा है और इस विशेष मामले में समस्या हल करता है लेकिन यह सभी मामलों में काम नहीं करता है। आइए मान लें, उदाहरण के लिए, मैं अलग-अलग क्षणों पर अलग-अलग 'बंद करने योग्य' ऑब्जेक्ट को बंद करना चाहता हूं। ऐसी स्थिति को संभालने के लिए मुझे अपने कार्यान्वयन में कुछ प्रकार की चाबियाँ जोड़नी होंगी ताकि 'करीबी()' अधिक दानेदार बन सकें। मेरी राय में ऐसी कार्यक्षमता भाषा द्वारा स्वयं इन सभी हैक्स के बिना प्रदान की जानी चाहिए। – Michael

+1

मुझे आमतौर पर एक और समस्या का सामना करना पड़ता है जब मुझे यह जांचने की आवश्यकता होती है कि 'Delegate.lazy' प्रॉपर्टी प्रारंभ की गई है या नहीं। अगर हमें संपत्ति के प्रतिनिधि तक पहुंच मिली तो इस समस्या को आसानी से सुलझाया जा सकता था, लेकिन सभी चलने वाले लोग बदसूरत लगते हैं। – Michael

+2

मुझे एहसास हुआ, आप प्रतिनिधि को एक अलग संपत्ति के रूप में घोषित कर सकते हैं और फिर दूसरी संपत्ति का प्रतिनिधि बन सकते हैं। इस तरह आप प्रतिनिधि को आसानी से एक्सेस कर सकते हैं। –

5

Kotlin 1.1 में (के बाद से बीटा 2), प्रतिनिधियों गुणों से प्राप्त किए जा सकें, इसलिए अब आप लिख सकते हैं

override fun close() { 
    (::stream.apply { isAccessible = true }.getDelegate() 
     as CloseableLazyVal<*>).close() 
} 
+0

हालांकि कोटलिन प्रतिबिंबित लाइब्रेरी की आवश्यकता है –