Каков наилучший способ определения константы 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, сгенерированный байт-код меньше, поскольку ему не нужно генерировать больше скрытых получателей, как описано в этой статье.