अधिकांश परिपक्व कोटलिन कोड में, आपको नीचे इनमें से एक पैटर्न मिलेगा। प्रॉपर्टी डेलीगेट्स का उपयोग करने वाला दृष्टिकोण सबसे छोटे कोड का उत्पादन करने के लिए कोटलिन की शक्ति का लाभ उठाता है।
नोट: यहाँ कोड है, java.util.Logging
लेकिन समान सिद्धांत किसी भी लॉगिंग लाइब्रेरी पर लागू होता है
स्टेटिक-लाइक (सामान्य, प्रश्न में आपके जावा कोड के बराबर)
यदि आप लॉगिंग सिस्टम के अंदर उस हैश लुकअप के प्रदर्शन पर भरोसा नहीं कर सकते हैं, तो आप अपने जावा कोड को एक साथी ऑब्जेक्ट का उपयोग करके समान व्यवहार प्राप्त कर सकते हैं जो एक उदाहरण को पकड़ सकता है और आपके लिए एक स्थिर की तरह महसूस कर सकता है।
class MyClass {
companion object {
val LOG = Logger.getLogger(MyClass::class.java.name)
}
fun foo() {
LOG.warning("Hello from MyClass")
}
}
आउटपुट बनाना:
Dec 26, 2015 11:28:32 AM org.stackoverflow.kotlin.test.MyClass
foo जानकारी: MyClass से नमस्कार
साथी के बारे में अधिक यहाँ वस्तुओं: कम्पेनियन वस्तुओं ... यह भी ध्यान रखें नमूने में है कि इसके बाद के संस्करण MyClass::class.java
प्रकार के उदाहरण हो जाता है Class<MyClass>
लकड़हारा के लिए है, जबकि this.javaClass
प्रकार के उदाहरण मिलेगा Class<MyClass.Companion>
।
एक कक्षा की प्रति घटना (सामान्य)
लेकिन, उदाहरण के स्तर पर कॉल करने और लकड़हारा होने से बचने का वास्तव में कोई कारण नहीं है। आपके द्वारा उल्लिखित मुहावरेदार जावा तरीका पुराना है और प्रदर्शन के डर पर आधारित है, जबकि प्रति वर्ग लकड़हारा पहले से ही ग्रह पर किसी भी उचित लॉगिंग सिस्टम द्वारा कैश किया जाता है। केवल लकड़हारा वस्तु को रखने के लिए एक सदस्य बनाएं।
class MyClass {
val LOG = Logger.getLogger(this.javaClass.name)
fun foo() {
LOG.warning("Hello from MyClass")
}
}
आउटपुट बनाना:
Dec 26, 2015 11:28:44 AM org.stackoverflow.kotlin.test.MyClass foo जानकारी: नमस्कार MyClass के लिए
आप उदाहरण और प्रति वर्ग भिन्नता दोनों का परीक्षण कर सकते हैं और यह देख सकते हैं कि अधिकांश ऐप्स के लिए यथार्थवादी अंतर है या नहीं।
संपत्ति के प्रतिनिधि (सामान्य, सबसे सुरुचिपूर्ण)
एक अन्य दृष्टिकोण, जिसे @Jire द्वारा दूसरे उत्तर में सुझाया गया है, एक संपत्ति प्रतिनिधि बनाना है, जिसका उपयोग आप तब किसी अन्य वर्ग में तर्क को समान रूप से करने के लिए कर सकते हैं जो आप चाहते हैं। ऐसा करने का एक सरल तरीका है क्योंकि कोटलिन Lazy
पहले से ही एक प्रतिनिधि प्रदान करता है , हम इसे केवल एक फ़ंक्शन में लपेट सकते हैं। यहाँ एक चाल यह है कि यदि हम वर्तमान में प्रतिनिधि का उपयोग कर रहे वर्ग के प्रकार को जानना चाहते हैं, तो हम इसे किसी भी वर्ग पर एक विस्तार समारोह बनाते हैं:
fun <R : Any> R.logger(): Lazy<Logger> {
return lazy { Logger.getLogger(unwrapCompanionClass(this.javaClass).name) }
}
// see code for unwrapCompanionClass() below in "Putting it all Together section"
यह कोड यह भी सुनिश्चित करता है कि यदि आप इसका उपयोग किसी कंपेनियन ऑब्जेक्ट में करते हैं, तो लकड़हारा का नाम वैसा ही होगा जैसा कि आपने इसे कक्षा में उपयोग किया था। अब आप बस:
class Something {
val LOG by logger()
fun foo() {
LOG.info("Hello from Something")
}
}
प्रति वर्ग उदाहरण के लिए, या यदि आप चाहते हैं कि यह प्रति वर्ग एक उदाहरण के साथ अधिक स्थिर हो:
class SomethingElse {
companion object {
val LOG by logger()
}
fun foo() {
LOG.info("Hello from SomethingElse")
}
}
और foo()
इन दोनों वर्गों पर कॉल करने से आपका आउटपुट होगा:
Dec 26, 2015 11:30:55 AM org.stackoverflow.kotlin.test.Something फू जानकारी: कुछ से नमस्ते
Dec 26, 2015 11:30:55 AM org.stackoverflow.kotlin.test.SomethingElse foo जानकारी: हेलो फ्रॉम समथिंग एल्स
एक्सटेंशन फ़ंक्शंस (किसी भी नामस्थान के "प्रदूषण" के कारण इस मामले में असामान्य)
कोटलिन के पास कुछ छुपी हुई तरकीबें हैं जो आपको इस कोड को कुछ और छोटा बना देती हैं। आप कक्षाओं पर एक्सटेंशन फ़ंक्शन बना सकते हैं और इसलिए उन्हें अतिरिक्त कार्यक्षमता दे सकते हैं। ऊपर टिप्पणियों में एक सुझाव Any
एक लकड़हारा समारोह के साथ विस्तार करना था । यह कभी भी शोर पैदा कर सकता है जब कोई किसी भी वर्ग में अपने आईडीई में कोड-पूरा करने का उपयोग करता है। लेकिन Any
कुछ अन्य मार्कर इंटरफ़ेस को विस्तारित करने के लिए एक गुप्त लाभ है : आप इसका मतलब यह कर सकते हैं कि आप अपनी खुद की कक्षा का विस्तार कर रहे हैं और इसलिए उस वर्ग का पता लगाएं जो आप भीतर हैं। है ना? कम भ्रमित होने के लिए, यहाँ कोड है:
// extend any class with the ability to get a logger
fun <T: Any> T.logger(): Logger {
return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
}
अब एक वर्ग (या साथी वस्तु) के भीतर, मैं इस विस्तार को अपनी कक्षा में कह सकता हूं:
class SomethingDifferent {
val LOG = logger()
fun foo() {
LOG.info("Hello from SomethingDifferent")
}
}
उत्पादन उत्पादन:
Dec 26, 2015 11:29:12 AM org.stackoverflow.kotlin.test.SomethingDifferent foo जानकारी: HelloDifDifferent से
मूल रूप से, कोड को एक्सटेंशन के लिए कॉल के रूप में देखा जाता है Something.logger()
। समस्या यह है कि निम्नलिखित अन्य वर्गों पर "प्रदूषण" बनाने में भी सच हो सकता है:
val LOG1 = "".logger()
val LOG2 = Date().logger()
val LOG3 = 123.logger()
मार्कर इंटरफ़ेस पर एक्सटेंशन फ़ंक्शंस (निश्चित नहीं कि "लक्षण" के लिए कितना सामान्य, लेकिन सामान्य मॉडल है)
एक्सटेंशन क्लीनर का उपयोग करने और "प्रदूषण" को कम करने के लिए, आप विस्तार करने के लिए एक मार्कर इंटरफ़ेस का उपयोग कर सकते हैं:
interface Loggable {}
fun Loggable.logger(): Logger {
return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
}
या यहां तक कि डिफ़ॉल्ट कार्यान्वयन के साथ इंटरफ़ेस का तरीका भी बनाएं:
interface Loggable {
public fun logger(): Logger {
return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
}
}
और अपनी कक्षा में इन विविधताओं का उपयोग करें:
class MarkedClass: Loggable {
val LOG = logger()
}
उत्पादन उत्पादन:
Dec 26, 2015 11:41:01 AM org.stackoverflow.kotlin.test.MarkedClass foo जानकारी: MarkedClass से नमस्कार
यदि आप लकड़हारे को रखने के लिए एक समान क्षेत्र के निर्माण के लिए बाध्य करना चाहते हैं, तो इस इंटरफ़ेस का उपयोग करते समय आपको आसानी से कार्यान्वयनकर्ता की आवश्यकता हो सकती है जैसे कि एक क्षेत्र LOG
:
interface Loggable {
val LOG: Logger // abstract required field
public fun logger(): Logger {
return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
}
}
अब इंटरफ़ेस के कार्यान्वयनकर्ता को इस तरह दिखना चाहिए:
class MarkedClass: Loggable {
override val LOG: Logger = logger()
}
बेशक, एक अमूर्त आधार वर्ग वही कर सकता है, जिसमें इंटरफ़ेस और अमूर्त वर्ग दोनों का विकल्प होता है जो उस इंटरफ़ेस को लागू करता है जो लचीलापन और एकरूपता की अनुमति देता है:
abstract class WithLogging: Loggable {
override val LOG: Logger = logger()
}
// using the logging from the base class
class MyClass1: WithLogging() {
// ... already has logging!
}
// providing own logging compatible with marker interface
class MyClass2: ImportantBaseClass(), Loggable {
// ... has logging that we can understand, but doesn't change my hierarchy
override val LOG: Logger = logger()
}
// providing logging from the base class via a companion object so our class hierarchy is not affected
class MyClass3: ImportantBaseClass() {
companion object : WithLogging() {
// we have the LOG property now!
}
}
यह सब एक साथ रखना (एक छोटा सहायक पुस्तकालय)
यहां एक छोटा सहायक पुस्तकालय है जो ऊपर दिए गए किसी भी विकल्प का उपयोग करने में आसान बनाता है। कोटलिन में एपीआई को विस्तारित करना और उन्हें अपनी पसंद के अनुसार बनाना सामान्य है। या तो विस्तार या शीर्ष स्तर के कार्यों में। लॉगर बनाने के लिए आपको विकल्प देने के लिए यहां एक मिश्रण है, और सभी विविधताओं को दिखाने वाला एक नमूना है:
// Return logger for Java class, if companion object fix the name
fun <T: Any> logger(forClass: Class<T>): Logger {
return Logger.getLogger(unwrapCompanionClass(forClass).name)
}
// unwrap companion class to enclosing class given a Java Class
fun <T : Any> unwrapCompanionClass(ofClass: Class<T>): Class<*> {
return ofClass.enclosingClass?.takeIf {
ofClass.enclosingClass.kotlin.companionObject?.java == ofClass
} ?: ofClass
}
// unwrap companion class to enclosing class given a Kotlin Class
fun <T: Any> unwrapCompanionClass(ofClass: KClass<T>): KClass<*> {
return unwrapCompanionClass(ofClass.java).kotlin
}
// Return logger for Kotlin class
fun <T: Any> logger(forClass: KClass<T>): Logger {
return logger(forClass.java)
}
// return logger from extended class (or the enclosing class)
fun <T: Any> T.logger(): Logger {
return logger(this.javaClass)
}
// return a lazy logger property delegate for enclosing class
fun <R : Any> R.lazyLogger(): Lazy<Logger> {
return lazy { logger(this.javaClass) }
}
// return a logger property delegate for enclosing class
fun <R : Any> R.injectLogger(): Lazy<Logger> {
return lazyOf(logger(this.javaClass))
}
// marker interface and related extension (remove extension for Any.logger() in favour of this)
interface Loggable {}
fun Loggable.logger(): Logger = logger(this.javaClass)
// abstract base class to provide logging, intended for companion objects more than classes but works for either
abstract class WithLogging: Loggable {
val LOG = logger()
}
इनमें से जो भी आप रखना चाहते हैं, चुनें और यहाँ उपयोग में सभी विकल्प हैं:
class MixedBagOfTricks {
companion object {
val LOG1 by lazyLogger() // lazy delegate, 1 instance per class
val LOG2 by injectLogger() // immediate, 1 instance per class
val LOG3 = logger() // immediate, 1 instance per class
val LOG4 = logger(this.javaClass) // immediate, 1 instance per class
}
val LOG5 by lazyLogger() // lazy delegate, 1 per instance of class
val LOG6 by injectLogger() // immediate, 1 per instance of class
val LOG7 = logger() // immediate, 1 per instance of class
val LOG8 = logger(this.javaClass) // immediate, 1 instance per class
}
val LOG9 = logger(MixedBagOfTricks::class) // top level variable in package
// or alternative for marker interface in class
class MixedBagOfTricks : Loggable {
val LOG10 = logger()
}
// or alternative for marker interface in companion object of class
class MixedBagOfTricks {
companion object : Loggable {
val LOG11 = logger()
}
}
// or alternative for abstract base class for companion object of class
class MixedBagOfTricks {
companion object: WithLogging() {} // instance 12
fun foo() {
LOG.info("Hello from MixedBagOfTricks")
}
}
// or alternative for abstract base class for our actual class
class MixedBagOfTricks : WithLogging() { // instance 13
fun foo() {
LOG.info("Hello from MixedBagOfTricks")
}
}
इस नमूने में बनाए गए लकड़हारे के सभी 13 उदाहरण एक ही लकड़हारा नाम और उत्पादन का उत्पादन करेंगे:
Dec 26, 2015 11:39:00 AM org.stackoverflow.kotlin.test.MixedBagOfTricks foo जानकारी: HelloBagOfTricks से नमस्कार
नोट:unwrapCompanionClass()
विधि दर्शाता है कि हम साथी वस्तु बल्कि संलग्नित क्लास के नाम पर एक लकड़हारा उत्पन्न नहीं है। साथी ऑब्जेक्ट वाली कक्षा को खोजने के लिए यह वर्तमान अनुशंसित तरीका है। नाम का उपयोग करके " $ साथी " स्ट्रिपिंग removeSuffix()
काम नहीं करता है क्योंकि साथी वस्तुओं को कस्टम नाम दिया जा सकता है।