In most mature Kotlin codes, you will find one of these patterns below. The property delegate approach leverages the power of Kotlin to create the smallest code.
Note: the code here is for java.util.Logging but the same theory applies to any log library
Static (regular, equivalent of your java code in question)
If you cannot trust the performance of this hash search in the logging system, you can get behavior similar to your Java code using a companion object that can hold an instance and feel static for you.
class MyClass { companion object { val LOG = Logger.getLogger(MyClass::class.java.name) } fun foo() { LOG.warning("Hello from MyClass") } }
output creation:
December 26, 2015 11:28:32 org.stackoverflow.kotlin.test.MyClass foo INFO: Hello from MyClass
More information about companion objects here: Companion Objects ... Also note that in the above example, MyClass::class.java gets an instance of type Class<MyClass> for the registrar, while this.javaClass will get an instance of type Class<MyClass.Companion> .
Per class instance (shared)
But there really is no reason to avoid calling and getting the registrar at the instance level. The idiomatic Java method you mentioned is deprecated and based on a fear of performance, while the logging facility for each class is already cached by almost any intelligent logging system on the planet. Just create a member to store the registrar object.
class MyClass { val LOG = Logger.getLogger(this.javaClass.name) fun foo() { LOG.warning("Hello from MyClass") } }
output creation:
December 26, 2015 11:28:44 org.stackoverflow.kotlin.test.MyClass foo INFO: Hello from MyClass
You can test the performance for both each instance and for each class, and see if there is a real difference for most applications.
Property delegates (ordinary, most elegant)
Another approach suggested by @Jire in another answer is to create a property delegate, which you can then use to evenly execute logic in any other class you want. There is an easier way to do this, since Kotlin already provides a Lazy delegate, we can just wrap it in a function. There is one trick here: if we want to find out the type of class that the delegate is currently using, we will make it an extension function for any class:
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"
This code also ensures that if you use it in a companion object, the registrar name will be the same as if you used it in the class itself. Now you can simply:
class Something { val LOG by logger() fun foo() { LOG.info("Hello from Something") } }
for each instance of the class, or if you want it to be more static with one instance per class:
class SomethingElse { companion object { val LOG by logger() } fun foo() { LOG.info("Hello from SomethingElse") } }
And your result of calling foo() for both of these classes will be:
December 26, 2015 11:30:55 org.stackoverflow.kotlin.test.Something foo INFORMATION: Hello from something
December 26, 2015 11:30:55 org.stackoverflow.kotlin.test.SomethingElse foo INFO: Hello from SomethingElse
Extension functions (unusual in this case due to the "pollution" of any namespace)
Kotlin has some hidden tricks that make this code even smaller. You can create extension functions for classes and therefore provide them with additional functionality. One suggestion in the comments above was the Any extension using the logger function. This can make noise at any time when someone uses code completion in their IDE in any class. But there is a secret advantage in extending the Any interface or some other token: you can imply that you are expanding your own class and therefore find the class you are in. BUT? To be less confusing, here is the code:
// extend any class with the ability to get a logger fun <T: Any> T.logger(): Logger { return Logger.getLogger(unwrapCompanionClass(this.javaClass).name) }
Now inside the class (or companion object), I can simply call this extension in my own class:
class SomethingDifferent { val LOG = logger() fun foo() { LOG.info("Hello from SomethingDifferent") } }
To produce products:
December 26, 2015 11:29:12 org.stackoverflow.kotlin.test.SomethingDifferent foo INFO: Hello from SomethingDifferent
Essentially, the code is seen as a call to the Something.logger() extension. The problem is that the following may also be true, creating “pollution” for other classes:
val LOG1 = "".logger() val LOG2 = Date().logger() val LOG3 = 123.logger()
Extension functions on the marker interface (not sure how widespread, but widespread model for "hell")
To make the use of extensions more understandable and to reduce "pollution", you can use the marker interface for the extension:
interface Loggable {} fun Loggable.logger(): Logger { return Logger.getLogger(unwrapCompanionClass(this.javaClass).name) }
Or even make the method part of the default implementation interface:
interface Loggable { public fun logger(): Logger { return Logger.getLogger(unwrapCompanionClass(this.javaClass).name) } }
And use any of these options in your class:
class MarkedClass: Loggable { val LOG = logger() }
To produce products:
December 26, 2015 11:41:01 org.stackoverflow.kotlin.test.MarkedClass foo INFO: Hello from MarkedClass
If you want to force the creation of a single field for storing the registrar, then using this interface you can easily require that the developer have a field, such as LOG :
interface Loggable { val LOG: Logger
The interface designer should now look like this:
class MarkedClass: Loggable { override val LOG: Logger = logger() }
Of course, an abstract base class can do the same, with the ability of both an interface and an abstract class that implements this interface, which provides flexibility and uniformity:
abstract class WithLogging: Loggable { override val LOG: Logger = logger() }
Putting it all together (a small helper library)
Here is a small helper library that makes it easy to use any of the options above. Kotlin has decided to extend the APIs to make them more suitable. Either in the extension, or in the functions of the top level. Here is a mix to give you options for creating registrars, and an example showing all the options:
// 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() }
Select any of the ones you want to save, and here are all the options:
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") } }
All 13 instances of registrars created in this sample will produce the same registrar name and output:
December 26, 2015 11:39:00 org.stackoverflow.kotlin.test.MixedBagOfTricks foo INFO: Hello from MixedBagOfTricks
Note. The unwrapCompanionClass() method ensures that we are not an unwrapCompanionClass() registrar named after a companion object, but rather a class. This is the current recommended way to find a class containing a companion object. Removing $ Companion from a name using removeSuffix() does not work, since companion objects can be given their own names.