Каков наилучший способ определения константы log TAG в Kotlin?
Я создаю свои первые классы Kotlin в своем приложении для Android. Обычно для ведения журнала я имею константу с именем TAG
. Что я должен делать на Java:
private static final String TAG = MyClass.class.getSimpleName();
Я знаю, что в классах Котлин я могу создать TAG
следующим образом:
private val TAG = MyClass::class.java.simpleName
Это нормально для проектов, которые используют Java и Kotlin, но что, если я начну новый проект только в Котлине? Как я могу определить там TAG
constant? Есть ли еще путь Котлина, где у меня нет этой странной конструкции class.java.simpleName
?
Ответы
Ответ 1
В общем случае константы - это все шапки (например, FOO) и расположены в сопутствующем объекте:
class MyClass {
companion object {
public const val FOO = 1
}
}
и определить поле TAG, которое вы можете использовать:
private val TAG = MyClass::class.qualifiedName
Ответ 2
Это расширение позволяет нам использовать TAG в любом классе
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")
Он также подтвержден для работы в качестве действительного тега журнала Android.
Ответ 3
Обычно предлагаемый подход использования companion object
генерирует дополнительный экземпляр static final
сопутствующего класса и, следовательно, плохая производительность и разумная память.
Лучший способ (IMHO)
Определите тег журнала как константу верхнего уровня, поэтому генерируется только дополнительный класс (MyClassKt
), но по сравнению с companion object
не будет его экземпляра static final
(и никакого экземпляра вообще):
private const val TAG = "MyLogTag"
class MyClass {
fun logMe() {
Log.w(TAG, "Message")
}
}
Другой вариант
Используйте обычный val
. Хотя это выглядит необычно, если тег журнала не является константой с верхним регистром, это не приведет к генерации каких-либо классов и имеет наименьшие издержки.
class MyClass {
private val tag = "myLogTag"
fun logMe() {
Log.w(tag, "Message")
}
}
Ответ 4
В Kotlin вы можете создать расширение и вместо этого вызывать тег как вызов метода. Это означает, что вам никогда не придется определять его внутри каждого класса, мы можем создавать его динамически каждый раз, когда вызываем метод:
inline fun <reified T> T.TAG(): String = T::class.java.simpleName
Ответ 5
Просто для меня это работало.
private val TAG = this::class.java.simpleName
Ответ 6
Вы можете определить свой TAG
помощью @JvmField
как @JvmField
ниже:
companion object {
@JvmField val TAG: String = MyClass::class.java.simpleName
}
Для более подробной информации, вы можете прочитать эту статью: Kotlin скрытых расходов
Ответ 7
Я создал несколько функций расширения Log, чтобы избежать объявления тега log, как мы делали в Java (возможно, менее производительного, но, учитывая, что мы говорим о ведении журнала, это должно быть приемлемым IMO). Подход использует параметры типа reified и другие полезности Kotlin для получения простого имени класса. Вот основной пример:
inline fun <reified T> T.logi(message: String) =
Log.i(T::class.java.simpleName, message)
Вы можете найти более сложную суть здесь
Ответ 8
Обновленный ответ с Kotlin 1.2.20
class MyClass {
companion object {
@JvmField
public val FOO = 1
}
}
использует
MyClass.FOO
Ответ 9
Мне нравится, что TAG
- это функция расширения, предложенная Фреди Медеросом.
расширяя свой ответ для поддержки анонимных классов:
/**
* extension function to provide TAG value
*/
val Any.TAG: String
get() {
return if (!javaClass.isAnonymousClass) {
val name = javaClass.simpleName
if (name.length <= 23) name else name.substring(0, 23)// first 23 chars
} else {
val name = javaClass.name
if (name.length <= 23) name else name.substring(name.length - 23, name.length)// last 23 chars
}
}
Ответ 10
Объявить переменную TAG с помощью 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
}
}
Используйте переменную типа
Log.d(YourClass.TAG, "Your message");
//or
Log.e(TAG, "Your message");
Ответ 11
Я нашел способ, который является более "copy-paste" -able, поскольку он не требует ввода имени вашего класса:
package com.stackoverflow.mypackage
class MyClass
{
companion object {
val TAG = this::class.toString().split(".").last().dropLast(10)
}
}
Это не самое элегантное решение, но оно работает.
this::class.toString().split(".").last()
даст вам "com.stackoverflow.mypackage.MyClass$Companion"
, поэтому вам нужно dropLast(10)
удалить $Companion
.
В качестве альтернативы вы можете сделать это:
package com.stackoverflow.mypackage
class MyClass
{
val TAG = this::class.simpleName
}
Но тогда переменная-член TAG
перестает быть "статической" и не соответствует рекомендуемым соглашениям об именах.
Ответ 12
Лучший способ войти (imho) - использовать Timber:
https://github.com/JakeWharton/timber
Но если вы не хотите использовать библиотеку, тогда
TAG может быть определен как свойство встроенного расширения (например, в Extensions.kt
):
inline val <reified T> T.TAG: String
get() = T::class.java.simpleName
Еще несколько расширений, чтобы не писать TAG все время в Log.d(TAG, "")
:
inline fun <reified T> T.logv(message: String) = Log.v(TAG, message)
inline fun <reified T> T.logi(message: String) = Log.i(TAG, message)
inline fun <reified T> T.logw(message: String) = Log.w(TAG, message)
inline fun <reified T> T.logd(message: String) = Log.d(TAG, message)
inline fun <reified T> T.loge(message: String) = Log.e(TAG, message)
И тогда вы можете использовать их в любом классе:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
logd("Activity created")
}
Ответ 13
Я создаю константу как объект-компаньон:
companion object {
val TAG = "SOME_TAG_VALUE"
}
Тогда я могу использовать его следующим образом:
MyClass.TAG
Ответ 14
AnkoLogger использует интерфейс для определения тега журнала.
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")
}
}
Вы можете использовать его следующим образом:
class MyClass : AnkoLogger {
fun someFun(){
info("logging info")
}
}
Возможно, AnkoLogger может дать вам некоторые идеи по внедрению специального инструмента ведения журнала.
Ответ 15
Используя идею Jpihl answer Я создал поле расширения для тега журнала.
Вот расширение:
val KClass<out Any>.logTag: String
get() {
val tagWithSuffix = this.toString().split(".").last()
val companionIndex = tagWithSuffix.indexOf("\$Companion")
return if (companionIndex >= 0) {
// Removes companion suffix and everything after it
tagWithSuffix.removeRange(companionIndex, tagWithSuffix.length)
} else {
// The tag is not created in companion object
tagWithSuffix
}
}
Теперь, когда мне нужно создать константу TAG в каком-то классе, я делаю это:
companion object {
private val TAG = this::class.logTag
}
Хорошо, что вы можете просто скопировать-вставить это, и он будет работать для каждого класса.
Ответ 16
В Android Studio обычным способом переименования чего-либо является щелчок правой кнопкой мыши по имени, выберите Refactor-> "Переименовать". Так что я думаю, что хорошо делать что-то подобное,
class MyClass {
companion object {
private const LOG_TAG = "MyClass"
}
}
потому что если вы переименуете класс MyClass
как я описал, то IDE предложит также переименовать вашу строку LOG_TAG.
В конечном счете, есть преимущества и недостатки использования этого метода по сравнению с другими методами. Поскольку LOG_TAG является строкой, нет необходимости импортировать kotlin-refle.jar, как если бы вы установили LOG_TAG
равным MyClass::class.simpleName
. Кроме того, поскольку переменная объявлена как константа времени компиляции с ключевым словом const
, сгенерированный байт-код меньше, поскольку ему не нужно генерировать больше скрытых получателей, как описано в этой статье.