可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm creating my first Kotlin classes in my Android application. Usually for logging purposes I have a constant with name TAG
. What I would do in Java is:
private static final String TAG = MyClass.class.getSimpleName();
I know that in Kotlin classes I can create TAG
using this way:
private val TAG = MyClass::class.java.simpleName
This is OK for projects that use Java and Kotlin but what if I start a new project that is only in Kotlin? How can I define there TAG
constant? Is there more Kotlin way where I don't have this strange construction class.java.simpleName
?
回答1:
In general constants are all caps (ex. FOO) and located in the companion object:
class MyClass {
companion object {
public const val FOO = 1
}
}
and to define the TAG field you can use:
private val TAG = MyClass::class.qualifiedName
回答2:
This extension allows us to use TAG in any class
val Any.TAG: String
get() {
val tag = javaClass.simpleName
return if (tag.length <= 23) tag else tag.substring(0, 23)
}
//usage
Log.e(TAG,"some value")
It it also validated to work as an Android valid Log tag.
回答3:
Commonly suggested approach of using the companion object
generates extra static final
instance of a companion class and thus is bad performance and memory-wise.
The best way (IMHO)
Define a log tag as a top-level constant, thus only extra class is generated (MyClassKt
), but compared to companion object
there will be no static final
instance of it (and no instance whatsoever):
private const val TAG = "MyLogTag"
class MyClass {
fun logMe() {
Log.w(TAG, "Message")
}
}
Another option
Use a normal val
. Though this looks unusual to see a log tag not as an all-uppercase constant, this will not generate any classes and has least overhead.
class MyClass {
private val tag = "myLogTag"
fun logMe() {
Log.w(tag, "Message")
}
}
回答4:
In Kotlin you could create an extension, and call tag as a method call instead. This would mean you'd never have to define it inside each class, we can construct it dynamically each time we call the method:
inline fun <reified T> T.TAG(): String = T::class.java.simpleName
回答5:
Simply doing the following worked for me.
private val TAG = this::class.java.simpleName
回答6:
You can define your TAG
by @JvmField
as below:
companion object {
@JvmField val TAG: String = MyClass::class.java.simpleName
}
For more details, you can read this article : Kotlin's hidden costs
回答7:
I created some Log extension functions to avoid declaring the log tag as we did in Java (maybe less performant, but given that we are talking about logging this should be acceptable IMO). The approach uses reified type parameters and other Kotlin goodies to retrieve the class simple name. Here is a basic example:
inline fun <reified T> T.logi(message: String) =
Log.i(T::class.java.simpleName, message)
You can find a more elaborated gist here
回答8:
Updated answer with Kotlin 1.2.20
class MyClass {
companion object {
@JvmField
public val FOO = 1
}
}
uses
MyClass.FOO
回答9:
Declare of TAG variable with val
class YourClass {
companion object {
//if use java and kotlin both in project
//private val TAG = MyClass::class.java.simpleName
//if use only kotlin in project
private val TAG = YourClass::class.simpleName
}
}
Use the variable like
Log.d(YourClass.TAG, "Your message");
//or
Log.e(TAG, "Your message");
回答10:
I found a way which is more "copy-paste"-able, since it doesn't require you to type the name of your class:
package com.stackoverflow.mypackage
class MyClass
{
companion object {
val TAG = this::class.toString().split(".").last().dropLast(10)
}
}
It's not the most elegant solution but it works.
this::class.toString().split(".").last()
will give you "com.stackoverflow.mypackage.MyClass$Companion"
so you need the dropLast(10)
to remove $Companion
.
Alternatively you can do this:
package com.stackoverflow.mypackage
class MyClass
{
val TAG = this::class.simpleName
}
But then the TAG
member variable is no longer "static" and doesn't follow the recommended naming conventions.
回答11:
I'm creating the constant as a companion object:
companion object {
val TAG = "SOME_TAG_VALUE"
}
Then, I can use it like this:
MyClass.TAG
回答12:
AnkoLogger uses an interface to define the log tag.
interface AnkoLogger {
/**
* The logger tag used in extension functions for the [AnkoLogger].
* Note that the tag length should not be more than 23 symbols.
*/
val loggerTag: String
get() = getTag(javaClass)
}
private fun getTag(clazz: Class<*>): String {
val tag = clazz.simpleName
return if (tag.length <= 23) {
tag
} else {
tag.substring(0, 23)
}
}
inline fun AnkoLogger.info(message: () -> Any?) {
val tag = loggerTag
if (Log.isLoggable(tag, Log.INFO)) {
Log.i(tag, message()?.toString() ?: "null")
}
}
You can use it like this:
class MyClass : AnkoLogger {
fun someFun(){
info("logging info")
}
}
Maybe AnkoLogger can give you some ideas to implement a custom logging tool.
回答13:
In Android Studio, the usual way to rename something is to right-click the name, select Refactor->Rename. So, I think it's fine to do something like this,
class MyClass {
companion object {
private const LOG_TAG = "MyClass"
}
}
because if you rename the class MyClass
like I described, then the IDE will suggest renaming your LOG_TAG String as well.
Ultimately there are pros and cons of using this method vs. other methods. Because LOG_TAG is a String, there's no need to import the kotlin-reflect.jar, as you would if you set LOG_TAG
equal to MyClass::class.simpleName
. Also because the variable is declared as a compile-time constant with the const
keyword, the generated bytecode is smaller since it doesn't need to generate more hidden getters, as described in this article.