Idiomatic way of logging in Kotlin - kotlin

Kotlin doesn't have the same notion of static fields as used in Java. In Java, the generally accepted way of doing logging is:
public class Foo {
private static final Logger LOG = LoggerFactory.getLogger(Foo.class);
}
Question is what is the idiomatic way of performing logging in Kotlin?

In the majority of mature Kotlin code, you will find one of these patterns below. The approach using Property Delegates takes advantage of the power of Kotlin to produce the smallest code.
Note: the code here is for java.util.Logging but the same theory applies to any logging library
Static-like (common, equivalent of your Java code in the question)
If you cannot trust in the performance of that hash lookup inside the logging system, you can get similar behavior to your Java code by using a companion object which can hold an instance and feel like a static to you.
class MyClass {
companion object {
val LOG = Logger.getLogger(MyClass::class.java.name)
}
fun foo() {
LOG.warning("Hello from MyClass")
}
}
creating output:
Dec 26, 2015 11:28:32 AM org.stackoverflow.kotlin.test.MyClass foo
INFO: Hello from MyClass
More on companion objects here: Companion Objects ... Also note that in the sample above MyClass::class.java gets the instance of type Class<MyClass> for the logger, whereas this.javaClass would get the instance of type Class<MyClass.Companion>.
Per Instance of a Class (common)
But, there is really no reason to avoid calling and getting a logger at the instance level. The idiomatic Java way you mentioned is outdated and based on fear of performance, whereas the logger per class is already cached by almost any reasonable logging system on the planet. Just create a member to hold the logger object.
class MyClass {
val LOG = Logger.getLogger(this.javaClass.name)
fun foo() {
LOG.warning("Hello from MyClass")
}
}
creating output:
Dec 26, 2015 11:28:44 AM org.stackoverflow.kotlin.test.MyClass foo
INFO: Hello from MyClass
You can performance test both per instance and per class variations and see if there is a realistic difference for most apps.
Property Delegates (common, most elegant)
Another approach, which is suggested by #Jire in another answer, is to create a property delegate, which you can then use to do the logic uniformly in any other class that you want. There is a simpler way to do this since Kotlin provides a Lazy delegate already, we can just wrap it in a function. One trick here is that if we want to know the type of the class currently using the delegate, we make it an extension function on 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 makes sure that if you use it in a Companion Object that the logger name will be the same as if you used it on the class itself. Now you can simply:
class Something {
val LOG by logger()
fun foo() {
LOG.info("Hello from Something")
}
}
for per class instance, 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 output from calling foo() on both of these classes would be:
Dec 26, 2015 11:30:55 AM org.stackoverflow.kotlin.test.Something foo
INFO: Hello from Something
Dec 26, 2015 11:30:55 AM org.stackoverflow.kotlin.test.SomethingElse foo
INFO: Hello from SomethingElse
Extension Functions (uncommon in this case because of "pollution" of Any namespace)
Kotlin has a few hidden tricks that let you make some of this code even smaller. You can create extension functions on classes and therefore give them additional functionality. One suggestion in the comments above was to extend Any with a logger function. This can create noise anytime someone uses code-completion in their IDE in any class. But there is a secret benefit to extending Any or some other marker interface: you can imply that you are extending your own class and therefore detect the class you are within. Huh? 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 within a class (or companion object), I can simply call this extension on my own class:
class SomethingDifferent {
val LOG = logger()
fun foo() {
LOG.info("Hello from SomethingDifferent")
}
}
Producing output:
Dec 26, 2015 11:29:12 AM org.stackoverflow.kotlin.test.SomethingDifferent foo
INFO: Hello from SomethingDifferent
Basically, the code is seen as a call to extension Something.logger(). The problem is that the following could also be true creating "pollution" on other classes:
val LOG1 = "".logger()
val LOG2 = Date().logger()
val LOG3 = 123.logger()
Extension Functions on Marker Interface (not sure how common, but common model for "traits")
To make the use of extensions cleaner and reduce "pollution", you could use a marker interface to extend:
interface Loggable {}
fun Loggable.logger(): Logger {
return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
}
Or even make the method part of the interface with a default implementation:
interface Loggable {
public fun logger(): Logger {
return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
}
}
And use either of these variations in your class:
class MarkedClass: Loggable {
val LOG = logger()
}
Producing output:
Dec 26, 2015 11:41:01 AM org.stackoverflow.kotlin.test.MarkedClass foo
INFO: Hello from MarkedClass
If you wanted to force the creation of a uniform field to hold the logger, then while using this interface you could easily require the implementer to have a field such as LOG:
interface Loggable {
val LOG: Logger // abstract required field
public fun logger(): Logger {
return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
}
}
Now the implementer of the interface must look like this:
class MarkedClass: Loggable {
override val LOG: Logger = logger()
}
Of course, an abstract base class can do the same, having the option of both the interface and an abstract class implementing that interface allows flexibility and uniformity:
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!
}
}
Putting it All Together (A small helper library)
Here is a small helper library to make any of the options above easy to use. It is common in Kotlin to extend API's to make them more to your liking. Either in extension or top-level functions. Here is a mix to give you options for how to create loggers, and a sample showing all variations:
// 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()
}
Pick whichever of those you want to keep, and here are all of the options in use:
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 the loggers created in this sample will produce the same logger name, and output:
Dec 26, 2015 11:39:00 AM org.stackoverflow.kotlin.test.MixedBagOfTricks foo
INFO: Hello from MixedBagOfTricks
Note: The unwrapCompanionClass() method ensures that we do not generate a logger named after the companion object but rather the enclosing class. This is the current recommended way to find the class containing the companion object. Stripping "$Companion" from the name using removeSuffix() does not work since companion objects can be given custom names.

Have a look at the kotlin-logging library.
It allows logging like that:
private val logger = KotlinLogging.logger {}
class Foo {
logger.info{"wohoooo $wohoooo"}
}
Or like that:
class FooWithLogging {
companion object: KLogging()
fun bar() {
logger.info{"wohoooo $wohoooo"}
}
}
I also wrote a blog post comparing it to AnkoLogger: Logging in Kotlin & Android: AnkoLogger vs kotlin-logging
Disclaimer: I am the maintainer of that library.
Edit: kotlin-logging now has multiplatform support: https://github.com/MicroUtils/kotlin-logging/wiki/Multiplatform-support

KISS: For Java Teams Migrating to Kotlin
If you don't mind providing the class name on each instantiation of the logger (just like java), you can keep it simple by defining this as a top-level function somewhere in your project:
import org.slf4j.LoggerFactory
inline fun <reified T:Any> logger() = LoggerFactory.getLogger(T::class.java)
This uses a Kotlin reified type parameter.
Now, you can use this as follows:
class SomeClass {
// or within a companion object for one-instance-per-class
val log = logger<SomeClass>()
...
}
This approach is super-simple and close to the java equivalent, but just adds some syntactical sugar.
Next Step: Extensions or Delegates
I personally prefer going one step further and using the extensions or delegates approach. This is nicely summarized in #JaysonMinard's answer, but here is the TL;DR for the "Delegate" approach with the log4j2 API (UPDATE: no need to write this code manually any more, as it has been released as an official module of the log4j2 project, see below). Since log4j2, unlike slf4j, supports logging with Supplier's, I've also added a delegate to make using these methods simpler.
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import org.apache.logging.log4j.util.Supplier
import kotlin.reflect.companionObject
/**
* An adapter to allow cleaner syntax when calling a logger with a Kotlin lambda. Otherwise calling the
* method with a lambda logs the lambda itself, and not its evaluation. We specify the Lambda SAM type as a log4j2 `Supplier`
* to avoid this. Since we are using the log4j2 api here, this does not evaluate the lambda if the level
* is not enabled.
*/
class FunctionalLogger(val log: Logger): Logger by log {
inline fun debug(crossinline supplier: () -> String) {
log.debug(Supplier { supplier.invoke() })
}
inline fun debug(t: Throwable, crossinline supplier: () -> String) {
log.debug(Supplier { supplier.invoke() }, t)
}
inline fun info(crossinline supplier: () -> String) {
log.info(Supplier { supplier.invoke() })
}
inline fun info(t: Throwable, crossinline supplier: () -> String) {
log.info(Supplier { supplier.invoke() }, t)
}
inline fun warn(crossinline supplier: () -> String) {
log.warn(Supplier { supplier.invoke() })
}
inline fun warn(t: Throwable, crossinline supplier: () -> String) {
log.warn(Supplier { supplier.invoke() }, t)
}
inline fun error(crossinline supplier: () -> String) {
log.error(Supplier { supplier.invoke() })
}
inline fun error(t: Throwable, crossinline supplier: () -> String) {
log.error(Supplier { supplier.invoke() }, t)
}
}
/**
* A delegate-based lazy logger instantiation. Use: `val log by logger()`.
*/
#Suppress("unused")
inline fun <reified T : Any> T.logger(): Lazy<FunctionalLogger> =
lazy { FunctionalLogger(LogManager.getLogger(unwrapCompanionClass(T::class.java))) }
// unwrap companion class to enclosing class given a Java Class
fun <T : Any> unwrapCompanionClass(ofClass: Class<T>): Class<*> {
return if (ofClass.enclosingClass != null && ofClass.enclosingClass.kotlin.companionObject?.java == ofClass) {
ofClass.enclosingClass
} else {
ofClass
}
}
Log4j2 Kotlin Logging API
Most of the previous section has been directly adapted to produce the Kotlin Logging API module, which is now an official part of Log4j2 (disclaimer: I am the primary author). You can download this directly from Apache, or via Maven Central.
Usage is basically as describe above, but the module supports both interface-based logger access, a logger extension function on Any for use where this is defined, and a named logger function for use where no this is defined (such as top-level functions).

As a good example of logging implementation I'd like to mention Anko which uses a special interface AnkoLogger which a class that needs logging should implement. Inside the interface there's code that generates a logging tag for the class. Logging is then done via extension functions which can be called inside the interace implementation without prefixes or even logger instance creation.
I don't think this is idiomatic, but it seems a good approach as it requires minimum code, just adding the interface to a class declaration, and you get logging with different tags for different classes.
The code below is basically AnkoLogger, simplified and rewritten for Android-agnostic usage.
First, there's an interface which behaves like a marker interface:
interface MyLogger {
val tag: String get() = javaClass.simpleName
}
It lets its implementation use the extensions functions for MyLogger inside their code just calling them on this. And it also contains logging tag.
Next, there is a general entry point for different logging methods:
private inline fun log(logger: MyLogger,
message: Any?,
throwable: Throwable?,
level: Int,
handler: (String, String) -> Unit,
throwableHandler: (String, String, Throwable) -> Unit
) {
val tag = logger.tag
if (isLoggingEnabled(tag, level)) {
val messageString = message?.toString() ?: "null"
if (throwable != null)
throwableHandler(tag, messageString, throwable)
else
handler(tag, messageString)
}
}
It will be called by logging methods. It gets a tag from MyLogger implementation, checks logging settings and then calls one of two handlers, the one with Throwable argument and the one without.
Then you can define as many logging methods as you like, in this way:
fun MyLogger.info(message: Any?, throwable: Throwable? = null) =
log(this, message, throwable, LoggingLevels.INFO,
{ tag, message -> println("INFO: $tag # $message") },
{ tag, message, thr ->
println("INFO: $tag # $message # $throwable");
thr.printStackTrace()
})
These are defined once for both logging just a message and logging a Throwable as well, this is done with optional throwable parameter.
The functions that are passed as handler and throwableHandler can be different for different logging methods, for example, they can write the log to file or upload it somewhere. isLoggingEnabled and LoggingLevels are omitted for brevity, but using them provides even more flexibility.
It allows for the following usage:
class MyClass : MyLogger {
fun myFun() {
info("Info message")
}
}
There is a small drawback: a logger object will be needed for logging in package-level functions:
private object MyPackageLog : MyLogger
fun myFun() {
MyPackageLog.info("Info message")
}

Would something like this work for you?
class LoggerDelegate {
private var logger: Logger? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): Logger {
if (logger == null) logger = Logger.getLogger(thisRef!!.javaClass.name)
return logger!!
}
}
fun logger() = LoggerDelegate()
class Foo { // (by the way, everything in Kotlin is public by default)
companion object { val logger by logger() }
}

Anko
You can use Anko library to do it. You would have code like below:
class MyActivity : Activity(), AnkoLogger {
private fun someMethod() {
info("This is my first app and it's awesome")
debug(1234)
warn("Warning")
}
}
kotlin-logging
kotlin-logging(Github project - kotlin-logging ) library allows you to write logging code like below:
class FooWithLogging {
companion object: KLogging()
fun bar() {
logger.info{"Item $item"}
}
}
StaticLog
or you can also use this small written in Kotlin library called StaticLog then your code would looks like:
Log.info("This is an info message")
Log.debug("This is a debug message")
Log.warn("This is a warning message","WithACustomTag")
Log.error("This is an error message with an additional Exception for output", "AndACustomTag", exception )
Log.logLevel = LogLevel.WARN
Log.info("This message will not be shown")\
The second solution might better if you would like to define an output format for logging method like:
Log.newFormat {
line(date("yyyy-MM-dd HH:mm:ss"), space, level, text("/"), tag, space(2), message, space(2), occurrence)
}
or use filters, for example:
Log.filterTag = "filterTag"
Log.info("This log will be filtered out", "otherTag")
Log.info("This log has the right tag", "filterTag")
timberkt
If you'd already used Jake Wharton's Timber logging library check timberkt.
This library builds on Timber with an API that's easier to use from Kotlin. Instead of using formatting parameters, you pass a lambda that is only evaluated if the message is logged.
Code example:
// Standard timber
Timber.d("%d %s", intVar + 3, stringFun())
// Kotlin extensions
Timber.d { "${intVar + 3} ${stringFun()}" }
// or
d { "${intVar + 3} ${stringFun()}" }
Check also: Logging in Kotlin & Android: AnkoLogger vs kotlin-logging
Hope it will help

That's what companion objects are for, in general: replacing static stuff.

What about an extension function on Class instead? That way you end up with:
public fun KClass.logger(): Logger = LoggerFactory.getLogger(this.java)
class SomeClass {
val LOG = SomeClass::class.logger()
}
Note - I've not tested this at all, so it might not be quite right.

First, you can add extension functions for logger creation.
inline fun <reified T : Any> getLogger() = LoggerFactory.getLogger(T::class.java)
fun <T : Any> T.getLogger() = LoggerFactory.getLogger(javaClass)
Then you will be able to create a logger using the following code.
private val logger1 = getLogger<SomeClass>()
private val logger2 = getLogger()
Second, you can define an interface that provides a logger and its mixin implementation.
interface LoggerAware {
val logger: Logger
}
class LoggerAwareMixin(containerClass: Class<*>) : LoggerAware {
override val logger: Logger = LoggerFactory.getLogger(containerClass)
}
inline fun <reified T : Any> loggerAware() = LoggerAwareMixin(T::class.java)
This interface can be used in the following way.
class SomeClass : LoggerAware by loggerAware<SomeClass>() {
// Now you can use a logger here.
}

create companion object and mark the appropriate fields with #JvmStatic annotation

There are many great answers here already, but all of them concern adding a logger to a class, but how would you do that to do logging in Top Level Functions?
This approach is generic and simple enough to work well in both classes, companion objects and Top Level Functions:
package nieldw.test
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import org.junit.jupiter.api.Test
fun logger(lambda: () -> Unit): Lazy<Logger> = lazy { LogManager.getLogger(getClassName(lambda.javaClass)) }
private fun <T : Any> getClassName(clazz: Class<T>): String = clazz.name.replace(Regex("""\$.*$"""), "")
val topLog by logger { }
class TopLevelLoggingTest {
val classLog by logger { }
#Test
fun `What is the javaClass?`() {
topLog.info("THIS IS IT")
classLog.info("THIS IS IT")
}
}

I have heard of no idiom in this regard.
The simpler the better, so I would use a top-level property
val logger = Logger.getLogger("package_name")
This practice serves well in Python, and as different as Kotlin and Python might appear, I believe they are quite similar in their "spirit" (speaking of idioms).

Slf4j example, same for others. This even works for creating package level logger
/**
* Get logger by current class name.
*/
fun getLogger(c: () -> Unit): Logger =
LoggerFactory.getLogger(c.javaClass.enclosingClass)
Usage:
val logger = getLogger { }

fun <R : Any> R.logger(): Lazy<Logger> = lazy {
LoggerFactory.getLogger((if (javaClass.kotlin.isCompanion) javaClass.enclosingClass else javaClass).name)
}
class Foo {
val logger by logger()
}
class Foo {
companion object {
val logger by logger()
}
}

This is still WIP (almost finished) so I'd like to share it:
https://github.com/leandronunes85/log-format-enforcer#kotlin-soon-to-come-in-version-14
The main goal of this library is to enforce a certain log style across a project. By having it generate Kotlin code I'm trying to address some of the issues mentioned in this question. With regards to the original question what I usually tend to do is to simply:
private val LOG = LogFormatEnforcer.loggerFor<Foo>()
class Foo {
}

You can simply build your own "library" of utilities. You don't need a large library for this task which will make your project heavier and complex.
For instance, you can use Kotlin Reflection to get the name, type and value of any class property.
First of all, make sure you have the meta-dependency settled in your build.gradle:
dependencies {
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}
Afterwards, you can simply copy and paste this code into your project:
import kotlin.reflect.full.declaredMemberProperties
class LogUtil {
companion object {
/**
* Receives an [instance] of a class.
* #return the name and value of any member property.
*/
fun classToString(instance: Any): String {
val sb = StringBuilder()
val clazz = instance.javaClass.kotlin
clazz.declaredMemberProperties.forEach {
sb.append("${it.name}: (${it.returnType}) ${it.get(instance)}, ")
}
return marshalObj(sb)
}
private fun marshalObj(sb: StringBuilder): String {
sb.insert(0, "{ ")
sb.setLength(sb.length - 2)
sb.append(" }")
return sb.toString()
}
}
}
Example of usage:
data class Actor(val id: Int, val name: String) {
override fun toString(): String {
return classToString(this)
}
}

For Kotlin Multiplaform logging I could not find a library that had all the features I needed so I ended up writing one. Please check out KmLogging. The features it implements is:
Uses platform specific logging on each platform: Log on Android, os_log on iOS, and console on JavaScript.
High performance. Only 1 boolean check when disabled. I like to put in lots of logging and want all of it turned off when release and do not want to pay much overhead for having lots of logging. Also, when logging is on it needs to be really performant.
Extensible. Need to be able add other loggers such as logging to Crashlytics, etc.
Each logger can log at a different level. For example, you may only want info and above going to Crashlytics and all other loggers disabled in production.
To use:
val log = logging()
log.i { "some message" }

Related

Kotlin type inference on "supposedly" right types

I am new to Kotlin and I was playing with it. I pretty much wanted to create a pretty basic event bus. So I came up with this
interface Event
interface EventListener<E : Event> {
fun handle(event: E)
}
interface EventBus {
fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>)
}
class MyBus() : EventBus {
private val eventListeners: MutableMap<String, MutableList<EventListener<out Event>>> = mutableMapOf()
constructor(listeners: List<Pair<Class<Event>, EventListener<Event>>>) : this() {
listeners.forEach {
registerListener(it.first, it.second)
}
}
override fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>) {
val key = aClass.name
val listeners: MutableList<EventListener<out Event>> = eventListeners.getOrPut(key) { mutableListOf() }
listeners.add(eventListener)
}
}
val bus = MyBus(
listOf(
MyEvent::class.java to MyEventListener()
)
)
class MyEvent : Event
class AnotherEvent : Event
class MyEventListener : EventListener<MyEvent> {
override fun handle(event: MyEvent) {
}
}
what happens is that when I try to create MyBus using the constructor accepting the list of pairs, I get
Type inference failed. Expected type mismatch: inferred type is List<Pair<Class<MyEvent>,MyEventListener>> but List<Pair<Class<Event>,EventListener<Event>>> was expected
But if I change the constructor to be something like
constructor(listeners: List<Pair<Class<out Event>, EventListener<out Event>>>) : this() {
listeners.forEach {
registerListener(it.first, it.second)
}
}
adding out pretty much everywhere, then the MyBus constructor works, but the invocation to registerListener(..) breaks for the same exact reason as before. So the only way to solve this is to add "out"s also on registerListener function.
I suspect I'm doing something wrong here, but I don't know what precisely. Any help?
If you want your EventListener to be able to consume Events, then its type has to be invariant or covariant (not declared out). If it let you pass your EventListener<MyEvent> as if it were an EventListener<Event>, then your MyBus class might call listener.handle(event) on it with some Event that is not a MyEvent, such as AnotherEvent. Then you will get a ClassCastException when it tries to cast this AnotherEvent to MyEvent.
To be able to store different types of invariant EventHandlers, you will have to remove the variance restrictions by using star projection, and cast them when you retrieve them from the map. So make the map keys into class objects instead of just Strings. Since you will not have the help of the compiler when working with the star-projected types, you need to be careful that you are only adding an item to your MutableMap that is of the same type as the Class key that's associated with it. Then when you retrieve items, only cast to an invariant type.
The other part of your issue is that your constructor needs a generic type. Right now it works exclusively with Event so it can't handle subtypes of Event. Kotlin doesn't (yet?) support generic types for constructors so you have to do this with a factory function.
Here's an example of all the above.
class MyBus() : EventBus {
private val eventListeners: MutableMap<Class<*>, MutableList<EventListener<*>>> = mutableMapOf()
override fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>) {
val listeners = retrieveListeners(aClass)
listeners.add(eventListener)
}
private fun <E: Event> retrieveListeners(aClass: Class<E>): MutableList<EventListener<E>> {
#Suppress("UNCHECKED_CAST")
return eventListeners.getOrPut(aClass) { mutableListOf() } as MutableList<EventListener<E>>
}
}
// Factory function
fun <E : Event> myBusOf(listeners: List<Pair<Class<E>, EventListener<E>>>): MyBus {
return MyBus().apply {
listeners.forEach {
registerListener(it.first, it.second)
}
}
}
And you might want to change the type of the factory parameter from a <List>Pair to a vararg Pair so it's easier to use.
Here's a stripped down example to explain the variance limitation.
Your interface for an Event consumer:
interface EventListener<E : Event> {
fun handle(event: E)
}
Two implementations of Event:
class HelloEvent: Event {
fun sayHello() = println("Hello world")
}
class BoringEvent: Event {}
A class implementing the interface:
class HelloEventListener: EventListener<HelloEvent> {
override fun handle(event: HelloEvent) {
event.sayHello()
}
}
Now you have an EventListener that can handle only HelloEvents. Try to treat it like an EventListener<Event>:
val eventListener: EventListener<Event> = HelloEventListener() // COMPILE ERROR!
Imagine the compiler did not prevent you from doing this and you do this:
val eventListener: EventListener<Event> = HelloEventListener()
eventListener.handle(BoringEvent()) // CLASS CAST EXCEPTION AT RUN TIME!
If this were allowed your HelloEventListener would try to call sayHello() on the BoringEvent, which doesn't have that function, so it will crash. This is what generics are here to protect you from.
Now suppose your HelloEventListener.handle() didn't call event.sayHello(). Well, then it could have safely handled a BoringEvent. But the compiler isn't doing that level of analysis for you. It just knows what you declared, that HelloEventListener cannot handle anything except HelloEvent.

Pass different generic types to function

I have a function in Kotlin that can take a generic object as a parameter. The two objects are unrelated and do not share any base types. They both however implement the same functions. I would like to re-use those functions within my function. Something along these lines:
fun storeUser(datastore: Any) {
datastore.storeName("John")
}
// Call the function
val datastore1 = DataStore1()
storeUser(datastore1)
val datastore2 = DataStore2()
storeUser(datastore2)
Both the DataStore1 and DataStore2 have a function called "storeName". Is there a way in Kotlin to re-use this function in the storeUser function? I tried playing around with Generics but this does not seem possible.
The example code above is simple. In my real app, there are many more functions beside storeName. If I can't have a common function to store my data, I will need to create two separate functions and duplicate the storage for both. That kind of sucks.
I recommend using a common interface for both classes. If they are provided by a thid-party library, you could wrap them in your own classes and interface.
If you don't want to do that, you could just check the type of the parameter in the storeUser function:
fun storeUser(datastore: Any) {
when(datastore) {
is DataStore1 -> datastore.storeName("John")
is DataStore2 -> datastore.storeName("John")
else -> throw IllegalArgumentException()
}
}
But note that if you have another datastore in the future, you will need to add one more is clause to this function. That makes this code not very maintainable...
Better solution
If you create an interface Datastore:
interface Datastore {
fun storeName(name: String)
}
and the make your datastores implement it:
class Datastore1 : Datastore {
//Datastore1.storeName implementation
}
class Datastore2 : Datastore {
//Datastore2.storeName implementation
}
Then, you don't need to check the types in storeUser function. Just change its parameter type to Datastore:
fun storeUser(datastore: Datastore) {
datastore.storeName("John")
}
If Datastore1 and Datastore2 are provided by a third-party library, you can wrap them in your own classes and implement your Datastore interface:
class FirstDatastore : Datastore {
private val datastore = DataStore1()
override fun storeName(name: String) {
datastore.storeName(name)
}
}
class SecondDatastore : Datastore {
private val datastore = DataStore2()
override fun storeName(name: String) {
datastore.storeName(name)
}
}
So you can call your function using your classes:
val datastore1 = FirstDatastore()
storeUser(datastore1)
val datastore2 = SecondDatastore()
storeUser(datastore2)
As I said in the comment to the question, it would really be better to write a common interface for these classes. If that's not possible because the classes come from an external dependency, the second best thing to do would be to wrap the code as Héctor did.
Kotlin is a statically typed language, so unfortunately wrapping code like this results in a lot of duplication. If you didn't want to write a new wrapper for every new instance of the DataStore, you could use reflection to call it dynamically. This way you only have to write the definition once. However, you forego all the compile-time benefits of static checks, so it's not really a good idea. It was good to do as an exercise though. 😎
class WrappedDataStore<T : Any>(private val dataStore: T) {
private fun callDynamically(methodName: String, vararg args: Any?) {
val argTypes = args.map { it?.let { it::class.java} }.toTypedArray()
dataStore.javaClass
.getMethod(methodName, *argTypes)
.invoke(dataStore, *args)
}
fun storeName(name: String) = callDynamically("storeName", name)
}
fun <T : Any> storeUser(dataStore: WrappedDataStore<T>) =
dataStore.storeName("John")
fun main() {
val one = WrappedDataStore(DataStore1())
val two = WrappedDataStore(DataStore2())
one.storeName("foo")
two.storeName("bar")
storeUser(one)
storeUser(two)
}
class DataStore1 {
fun storeName(foo: String) = println("DataStore1 $foo")
}
class DataStore2 {
fun storeName(bar: String) = println("DataStore2 $bar")
}
Output:
DataStore1 foo
DataStore2 bar
DataStore1 John
DataStore2 John

Implement #slf4j annotation from Lombok in Kotlin

Is there a way to implement something similar to Lombok's annotation #slf4j with annotation class in Kotlin?
Right now I have an extension function that instantiates a Logger Factory for me, and I must create these variables in each of my classes just like the example below:
#RestController
#RequestMapping("/api/v1/sample")
class SampleController() {
private val log = logger()
#GetMapping
fun show(): String {
log.info("SOME LOGGING MESSAGE")
return "OK"
}
}
inline fun <reified T> T.logger(): Logger {
if (T::class.isCompanion) {
return LoggerFactory.getLogger(T::class.java.enclosingClass)
}
return LoggerFactory.getLogger(T::class.java)
}
What I want to achieve is something like:
#Logger
#RestController
#RequestMapping("/api/v1/sample")
class SampleController() {
#GetMapping
fun show(): String {
log.info("SOME LOGGING MESSAGE")
return "OK"
}
}
made this pretty thing yesterday
#Target(AnnotationTarget.CLASS)
#Retention(AnnotationRetention.RUNTIME)
annotation class Log {
companion object {
inline var <reified T> T.log: Logger
get() = LoggerFactory.getLogger(T::class.java)
set(value) {}
}
}
edit:
Don't use this mess above. use
companion object {
private val log: Logger = LoggerFactory.getLogger(this::class.java)
}
see Idiomatic way of logging in Kotlin
Turning Danny Lagrouw's comment into an answer:
You can get a similar to #Slf4j low-boilerplate solution that does not require you to specify the class manually by using Micro Utils' kotlin-logging
import mu.KotlinLogging
private val log = KotlinLogging.logger {}
class LoggingDemoClass() {
fun logSometing() {
log.info("Logging like a pro!")
}
}
Caveat: I've only just started using it but and haven't investigated any of the kotlin sugar it puts on top of Slf4j but so far it seems to handle well enough as a drop-in replacement for #Slf4j in kotlin code.

Abstract Class vs. Lambda parameters

Since kotlin has such good support for lambdas, I started to use lambdas as constructor parameters for abstract classes instead of declaring abstract val/fun.
It's more concise in my opinion, especially because val type get's inferred.
What are the downsides to this?
abstract class AbstractListScreen<T> (
val data: Set<T>,
val filterators: (T) -> Set<String>
) {
fun open() {
/* ... */
}
}
class OrderListScreen : AbstractListScreen<Data>(data = setOf(),
filterators = { setOf(it.toString()) }
) {
fun someEvent() {
/* ...*/
}
}
In your example, each instance of OrderListScreen will create its own filterators instance of function type (T) -> Set<String>. This has additional run-time overhead in both memory and performance when compared with abstract functions and their overrides which are stored in the type definition at compile-time.
The default filterators can be stored in a property to reduce this run-time overhead:
class OrderListScreen : AbstractListScreen<Data>(data = setOf(),
filterators = defaultFilterators
) {
companion object {
val defaultFilterators: (Data) -> Set<String> = { setOf(it.toString()) }
}
fun someEvent() {
/* ...*/
}
}
However, each instance of OrderListScreen will still have its own reference to defaultFilterators which is still additional run-time overhead (although marginal unless you have many instances of these types).
Function types such as (T) -> Set<String> may have named parameters (e.g. (element: T) -> Set<String>) but currently IDEs such as IntelliJ IDEA do not use those named parameters in generated documentation or code stubs so such information is lost when subclassing, etc. IDEs do use named parameters in generated documentation and code stubs for abstract functions.
You cannot (currently) associate documentation directly with the function type parameter which you can do with abstract functions.
When attempting to account for the run-time overhead the code doesn't look much different when using abstract functions, the run-time overhead is eliminated, and current IDE support for generated code stubs, documentation, etc. is improved:
abstract class AbstractListScreen<T>(val data: Set<T>) {
abstract fun filterators(element: T): Set<String>
fun open() {
/* ... */
}
}
class OrderListScreen : AbstractListScreen<Data>(data = setOf()) {
override fun filterators(element: Data): Set<String> = setOf(element.toString())
fun someEvent() {
/* ...*/
}
}

Create an annotation instance in Kotlin

I have a framework written in Java that, using reflection, get the fields on an annotation and make some decisions based on them. At some point I am also able to create an ad-hoc instance of the annotation and set the fields myself. This part looks something like this:
public #interface ThirdPartyAnnotation{
String foo();
}
class MyApp{
ThirdPartyAnnotation getInstanceOfAnnotation(final String foo)
{
ThirdPartyAnnotation annotation = new ThirdPartyAnnotation()
{
#Override
public String foo()
{
return foo;
}
};
return annotation;
}
}
Now I am trying to do the exact thing in Kotlin. Bear in mind that the annotation is in a third party jar.
Anyway, here is how I tried it in Kotlin:
class MyApp{
fun getAnnotationInstance(fooString:String):ThirdPartyAnnotation{
return ThirdPartyAnnotation(){
override fun foo=fooString
}
}
But the compiler complains about: Annotation class cannot be instantiated
So the question is: how should I do this in Kotlin?
You can do this with Kotlin reflection:
val annotation = ThirdPartyAnnotation::class.constructors.first().call("fooValue")
In the case of annotation having no-arg constructor (e.g. each annotation field has a default value), you can use following approach:
annotation class SomeAnnotation(
val someField: Boolean = false,
)
val annotation = SomeAnnotation::class.createInstance()
This is the solution I might have found but feels like a hack to me and I would prefer to be able to solve it within the language.
Anyway, for what is worth,it goes like this:
class MyApp {
fun getInstanceOfAnnotation(foo: String): ThirdPartyAnnotation {
val annotationListener = object : InvocationHandler {
override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
return when (method?.name) {
"foo" -> foo
else -> FindBy::class.java
}
}
}
return Proxy.newProxyInstance(ThirdPartyAnnotation::class.java.classLoader, arrayOf(ThirdPartyAnnotation::class.java), annotationListener) as ThirdPartyAnnotation
}
}