Константи в Котліні - який рекомендований спосіб їх створення?


165

Як рекомендується створювати константи в Котліні? І що таке конвенція про іменування? Я цього не знайшов у документації.

companion object {
    //1
    val MY_CONST = "something"

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

Або ...?


4
Якщо ви хочете щось відповідати public static finalполі на Java, використовуйте const valв своєму супутниковому об'єкті. Якщо ви хочете private static finalпольовий і загальнодоступний користувач, використовуйте valу своєму супутниковому об’єкті.
Майкл

2
Ось блогпост, який пояснює способи визначення констант у Котліні
Майсер

Оформити цю статтю . Він дає хороший огляд різних способів зберігання ваших констант із відповідними компромісами.
звільнений сержант

Відповіді:


132

У Котліні, якщо ви хочете створити локальні константи, які повинні бути використані в класі, ви можете створити їх, як показано нижче

val MY_CONSTANT = "Constants"

І якщо ви хочете створити загальнодоступну константу в kotlin, як public static final у java, ви можете створити її наступним чином.

companion object{

     const val MY_CONSTANT = "Constants"

}

3
Як я можу використовувати його в окремому файлі, як новий ім'я Constants.ktабо як?
Naveed Abbas

2
Я використовую файл для констант. тримайте там усі мої константи.
filthy_wizard

2
вам не потрібно, companion objectя думаю, відповідь @piotrpo має бути прийнятою
К'яра

@Chiara супутнім об'єктом (та його класом, що додається) служить простором імен на відміну від декларацій верхнього рівня. Я думаю, що обидві відповіді можуть мати сенс в залежності від ситуації.
jingx

@jingx так, у вас є пункт для додавання до нього простору імен. : +1:
К'яра

118

Уникайте використання супутніх предметів. За кришкою створені методи екземпляра getter та setter, щоб поля були доступними. Виклик примірників методів технічно дорожче, ніж виклик статичних методів.

public class DbConstants {
    companion object {
        val TABLE_USER_ATTRIBUTE_EMPID = "_id"
        val TABLE_USER_ATTRIBUTE_DATA = "data"
    }

Замість цього визначте константи в object.

Рекомендована практика :

object DbConstants {
        const val TABLE_USER_ATTRIBUTE_EMPID = "_id"
        const val TABLE_USER_ATTRIBUTE_DATA = "data"
}

і отримати доступ до них у всьому світі так: DbConstants.TABLE_USER_ATTRIBUTE_EMPID


Чи не супутній об’єкт є особливим випадком об'єкта? Як const valоб’єкт супутника може бути інакшим, ніж const valу звичайному об'єкті (тобто, різницею між вашими прикладами, здається, є те, що ви опущені constу випадку супутнього об’єкта - якщо ви додасте const, приклади повинні мати однакову ефективність)
Ервін Болвідт

1
@ErwinBolwidt Я думаю, що пункт @ sudesh полягає в тому, що не слід використовувати дизайн об'єктів-об'єктів-супутників-об'єктів, коли єдиною метою структури є надання простору імен для деяких постійних значень. Але якщо ваша структура повинна бути миттєвою, а також додавати пару const valс, оголошення a companion objectє правильним.
Арі Лаценський

7
@ErwinBolwidt: Судеш має рацію, генерований байтовий код для супутніх об'єктів передбачає додаткове створення об'єктів з геттерами під кришкою. Хороше пояснення з прикладами декомпільованого котліна див. Blog.egorand.me/where-do-i-put-my-constants-in-kotlin
dominik

2
дякую @dominik, це дуже детальна стаття, я рекомендую це всім, хто хоче це глибше зрозуміти, є багато випадків, коли kotlin виробляє неоптимальний байт-код, jetbrains вирішив багато таких помилок, пов’язаних з продуктивністю ... слідкуйте за обговоренням .kotlinlang.org , вам буде повідомлено про багато таких основних аспектів.
sudesh

1
Я дізнався щось багато з вашої відповіді сьогодні @sudesh спасибі!
Рахі Давале

34

Перш за все , умова іменування в Котліні для констант така ж, як у java (наприклад: MY_CONST_IN_UPPERCASE).

Як я повинен його створити?

1. Як значення верхнього рівня (рекомендується)

Потрібно просто поставити конст за межі своєї декларації класу.

Дві можливості : оголосити про свій клас у файлі класу (const має чітке відношення до класу)

private const val CONST_USED_BY_MY_CLASS = 1

class MyClass { 
    // I can use my const in my class body 
}

Створіть виділений файл constants.kt, де зберігати ці глобальні const (тут ви хочете широко використовувати const у своєму проекті):

package com.project.constants
const val URL_PATH = "https:/"

Тоді вам просто потрібно імпортувати його там, де вам потрібно:

import com.project.constants

MyClass {
    private fun foo() {
        val url = URL_PATH
        System.out.print(url) // https://
    }
}

2. Оголосити його в супутнім об’єкті (або в об'єктній декларації)

Це набагато менш чисто, тому що під кришкою, коли байтовий код генерується, створюється марний об'єкт:

MyClass {
    companion object {
        private const val URL_PATH = "https://"
        const val PUBLIC_URL_PATH = "https://public" // Accessible in other project files via MyClass.PUBLIC_URL_PATH
    }
}

Ще гірше, якщо ви оголосите його як val замість const (компілятор генерує марний об'єкт + марну функцію):

MyClass {
    companion object {
        val URL_PATH = "https://"
    }
}

Примітка :

У котліні const може містити просто примітивні типи. Якщо ви хочете передати йому функцію, вам потрібно додати анотацію @JvmField. Під час компіляції вона буде перетворена як публічна статична кінцева змінна. Але це повільніше, ніж з примітивним типом. Намагайтеся уникати цього.

@JvmField val foo = Foo()

це має бути прийнятою відповіддю. у будь-якому випадку у випадку типу: загальнодоступний статичний кінцевий шаблон REGEX_NOTEMPTY = Pattern.compile (". +") ????
Xan

23

Значення, відомі під час компіляції, можна (і на мій погляд, повинні) позначати як постійні.

Конвенції іменування повинні відповідати Java і повинні бути належним чином видимими при використанні з коду Java (це якось важко досягти із супутніми об'єктами, але все одно).

Належними постійними деклараціями є:

const val MY_CONST = "something"
const val MY_INT = 1

3
Naming conventions should follow Java ones- чому?
Джодіморо

3
Котлін зазвичай дотримується стандартів Java за замовчуванням, якщо не вказано інше, щоб зробити інтероп гладким.
zsmb13

4
Вказується так у документації @Jodimoro kotlinlang.org/docs/reference/coding-conventions.html
Ніл

2
@Neil, це не так.
Джодіморо

13
У цьому посиланні я розмістив, вони кажутьIf in doubt, default to the Java Coding Conventions
Ніл

16

Для декларування констант у Котліні вам не потрібен клас, об’єкт або супутні об’єкти. Ви можете просто оголосити файл, у якому є всі константи (наприклад, Constants.kt або ви також можете помістити їх у будь-який існуючий файл Котліна) і безпосередньо оголосити константи всередині файлу. Константи, відомі під час компіляції, повинні бути позначені знаком const.

Отже, у цьому випадку це повинно бути:

const val MY_CONST = "something"

а потім можна імпортувати константу, використовуючи:

import package_name.MY_CONST

Ви можете посилатися на це посилання


13
Константи повинні бути в класі, до якого вони пов'язані. Якщо ви складете клас "Константи", ви закінчите, зрештою, сотні констант всередині нього. Pe: MAX_WIDTH, MAX_HEIGHT повинен бути в класі Screen, щоб ви могли до нього отримати доступ логічно: Screen.MAX_WIDTH і вам не потрібно ставити Constants.SCREEN_MAX_WIDTH, який буде продубльовано константами.SCR_MAX_W і Constants.MAX_WIDTH через 2 роки, оскільки NOBODY прокручує сотні / тисячі ліній вниз, коли вони натискають Ctrl + простір для автоматичного завершення. Серйозно: не робіть цього. призводить до
незбагненності

1
@inigoD Це правда, якщо ви використовуєте константу в одному місці або тільки у дітей, але це навряд чи так. Якщо ви помістите константу в незрозумілий клас, то забудете про це або, швидше за все, переймете кодову базу, ви можете їх дублювати. Або не очевидно, куди їх поставити. Джерело чи пункт призначення? Ви можете створити кілька постійних файлів, які легко знайти. Один для ключів уподобань, один для ключів запиту, другий для констант перегляду тощо
Herrbert74

1
@ Herrbert74 Вибачте, але я з вами повинен погодитися. Я погоджуюся з тим, що іноді важко знайти те, що є, але постійним місцем завжди повинен бути клас, який більше пов'язаний з ним. І збереження їх випадковим чином у файлах із випадковим числом - це не найкращий спосіб, якщо ви хочете отримати їх пізніше ... Ви стверджуєте, що вони зберігаються не випадковим чином, але в пакунках, до яких константи пов'язані, але це лише привід для не кладіть їх у класи, з якими вони пов'язані, зрештою, їх місце ...
inigoD

4
Якщо константа по-справжньому глобальна або має великий обсяг ... наприклад, значення анотації, що використовується у всіх пакунках, або ім'я заголовка, яке отримує кілька контролерів тощо, тоді цілком прийнятно створювати "константи" клас ", який визначається відповідним чином. Однак константи, які використовуються лише у конкретних контекстах, слід підходити до цього контексту та оголошувати у відповідному класі.
Nephthys76

@ Nephthys76 Як і примітка, для " такого, як значення для анотації, що використовується у всіх пакунках ", я б сказав, що найкраще місце для константи - це клас анотацій.
Забій

8

Якщо ви поставите свою const val valName = valValueназву перед назвою класу, таким чином вона створить

public static final YourClass.Ktякі матимуть public static finalзначення.

Котлін :

const val MY_CONST0 = 0
const val MY_CONST1 = 1
data class MyClass(var some: String)

Java декомпілюється:

public final class MyClassKt {
    public static final int MY_CONST0 = 0;
    public static final int MY_CONST1 = 1;
}
// rest of MyClass.java

Це правда? Хтось має досвід із цим методом?
Скотт Біггс

5
class Myclass {

 companion object {
        const val MYCONSTANT = 479
}

у вас є два варіанти, ви можете використовувати constключове слово або використовувати те, @JvmFieldщо робить його статичною кінцевою константою Java.

class Myclass {

     companion object {
           @JvmField val MYCONSTANT = 479
    }

Якщо ви використовуєте @JvmFieldанотацію, то після її складання константа вводиться для вас так, як ви б назвали її в Java.
Так само, як ви б назвали це в java, компілятор замінить це для вас, коли ви викликаєте константу супутника в коді.

Однак якщо ви використовуєте ключове слово const, то значення константи стає вкладеним. Під inline я маю на увазі, що фактичне значення використовується після його складання.

Отже, підсумовуючи ось що робитиме компілятор для вас:

//so for @JvmField:

Foo var1 = Constants.FOO;

//and for const:

Foo var1 = 479

5

Котлін оголошує статичне та постійне значення & метод

object MyConstant {

@JvmField   // for access in java code 
val PI: Double = 3.14

@JvmStatic // JvmStatic annotation for access in java code
fun sumValue(v1: Int, v2: Int): Int {
    return v1 + v2
}

}

Значення доступу в будь-якому місці

val value = MyConstant.PI
val value = MyConstant.sumValue(10,5)

1
як визначити глобальний чи статичний метод?
Самад Талукдер

@SamadTalukder У Котліні буде весело sumValue (v1: Int, v2: Int): Int {return v1 + v2}
Shomu

5

Мовляв val, змінні, визначені constключовим словом, незмінні. Різниця тут полягає в тому, що constвикористовується для змінних, які відомі під час компіляції.

Оголошення змінної constдуже схоже на використання staticключового слова на Java.

Давайте подивимося, як оголосити змінну const у Котліні:

const val COMMUNITY_NAME = "wiki"

І аналогічним кодом, написаним на Java, було б:

final static String COMMUNITY_NAME = "wiki";

Додавання до відповідей вище -

@JvmField використовується для вказівки компілятора Kotlin не генерувати getters / setters для цього властивості та не виставляти його як поле.

 @JvmField
 val COMMUNITY_NAME: "Wiki"

Статичні поля

Властивості Котліна, оголошені в іменованому об'єкті або супутниковому об'єкті, матимуть статичні поля резервування або в цьому названому об'єкті, або в класі, що містить супутній об'єкт.

Зазвичай ці поля є приватними, але їх можна відкрити одним із наступних способів:

  • @JvmField анотація;
  • lateinit модифікатор;
  • const модифікатор.

Детальніше тут - https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instan-fields


4

Щось, що не згадується в жодній з відповідей, - це надмірні витрати companion objects. Як ви можете прочитати тут , супутні об’єкти насправді є об'єктами, і їх створення споживає ресурси. Крім того, вам може знадобитися проходити кілька функцій геттера кожен раз, коли ви використовуєте свій констант. Якщо все, що вам потрібно, це кілька примітивних констант, вам, ймовірно, просто краще скористатися, valщоб отримати кращі показники та уникнути companion object.

TL; DR; статті:

Використання супутнього об'єкта фактично перетворює цей код

class MyClass {

    companion object {
        private val TAG = "TAG"
    }

    fun helloWorld() {
        println(TAG)
    }
}

У цей код:

public final class MyClass {
    private static final String TAG = "TAG";
    public static final Companion companion = new Companion();

    // synthetic
    public static final String access$getTAG$cp() {
        return TAG;
    }

    public static final class Companion {
        private final String getTAG() {
            return MyClass.access$getTAG$cp();
        }

        // synthetic
        public static final String access$getTAG$p(Companion c) {
            return c.getTAG();
        }
    }

    public final void helloWorld() {
        System.out.println(Companion.access$getTAG$p(companion));
    }
}

Тому намагайтеся уникати їх.


3

місцеві константи:

const val NAME = "name"

Глобальні константи:

object MyConstants{
    val NAME = "name"
    val ID = "_id"
    var EMAIL = "email"
}

отримати доступ до MyConstants.NAME


1

Є кілька способів визначити константи в Котліні,

Використання супутнього об’єкта

    companion object {
        const val ITEM1 = "item1"
        const val ITEM2 = "item2"
    }

ви можете використовувати вищевказаний об'єктний блок всередині будь-якого класу та визначити всі ваші поля всередині цього блоку. Але в цьому підході є проблема, документація говорить:

незважаючи на те, що члени об'єктів-супутників схожі на статичні члени іншими мовами, під час виконання вони все ще є членами екземплярів реальних об'єктів і можуть, наприклад, реалізувати інтерфейси.

Коли ви створюєте константи за допомогою супутнього об’єкта і бачите розкомпільований байт-код , ви будете щось схоже нижче

  ClassName.Companion Companion = ClassName.Companion.$$INSTANCE;
  @NotNull
  String ITEM1 = "item1";
  @NotNull
  String ITEM2 = "item2";

  public static final class Companion {
     @NotNull
     private static final String ITEM1 = "item1";
     @NotNull
     public static final String ITEM2 = "item2";

     // $FF: synthetic field
     static final ClassName.Companion $$INSTANCE;

     private Companion() {
     }

     static {
        ClassName.Companion var0 = new ClassName.Companion();
        $$INSTANCE = var0;
     }
  }

Звідси ви легко бачите, що сказано в документації, навіть якщо члени супутніх об'єктів виглядають як статичні члени на інших мовах, під час виконання вони все ще є членами екземплярів реальних об'єктів. Це робить додаткову роботу, ніж потрібно.

Тепер з'явився інший спосіб, коли нам не потрібно використовувати супутні об'єкти, як показано нижче,

object ApiConstants {
      val ITEM1: String = "item1"
 }

Знову, якщо ви побачите декомпільовану версію байтового коду вищевказаного фрагмента, ви знайдете щось подібне,

public final class ApiConstants {
     private static final String ITEM1 = "item1";

     public static final ApiConstants INSTANCE;

     public final String getITEM1() {
           return ITEM1;
      }

     private ApiConstants() {
      }

     static {
         ApiConstants var0 = new ApiConstants();
         INSTANCE = var0;
         CONNECT_TIMEOUT = "item1";
      }
    }

Тепер, якщо ви бачите вищезгаданий декомпільований код, він створює метод get для кожної змінної. Цей метод отримання взагалі не потрібен.

Щоб позбутися цих методів отримання , слід використовувати const перед вал, як нижче,

object ApiConstants {
     const val ITEM1: String = "item1"
 }

Тепер, якщо ви побачите декомпільований код вище фрагменту, вам буде легше його прочитати, оскільки це робить найменше фонове перетворення для вашого коду.

public final class ApiConstants {
    public static final String ITEM1 = "item1";
    public static final ApiConstants INSTANCE;

    private ApiConstants() {
     }

    static {
        ApiConstants var0 = new ApiConstants();
        INSTANCE = var0;
      }
    }

Тож це найкращий спосіб створення констант.


0

Для примітивів і струн:

/** The empty String. */
const val EMPTY_STRING = ""

Для інших випадків:

/** The empty array of Strings. */
@JvmField val EMPTY_STRING_ARRAY = arrayOfNulls<String>(0)

Приклад:

/*
 * Copyright 2018 Vorlonsoft LLC
 *
 * Licensed under The MIT License (MIT)
 */

package com.vorlonsoft.android.rate

import com.vorlonsoft.android.rate.Constants.Utils.Companion.UTILITY_CLASS_MESSAGE

/**
 * Constants Class - the constants class of the AndroidRate library.
 *
 * @constructor Constants is a utility class and it can't be instantiated.
 * @since       1.1.8
 * @version     1.2.1
 * @author      Alexander Savin
 */
internal class Constants private constructor() {
    /** Constants Class initializer block. */
    init {
        throw UnsupportedOperationException("Constants$UTILITY_CLASS_MESSAGE")
    }

    /**
     * Constants.Date Class - the date constants class of the AndroidRate library.
     *
     * @constructor Constants.Date is a utility class and it can't be instantiated.
     * @since       1.1.8
     * @version     1.2.1
     * @author      Alexander Savin
     */
    internal class Date private constructor() {
        /** Constants.Date Class initializer block. */
        init {
            throw UnsupportedOperationException("Constants.Date$UTILITY_CLASS_MESSAGE")
        }

        /** The singleton contains date constants. */
        companion object {
            /** The time unit representing one year in days. */
            const val YEAR_IN_DAYS = 365.toShort()
        }
    }

    /**
     * Constants.Utils Class - the utils constants class of the AndroidRate library.
     *
     * @constructor Constants.Utils is a utility class and it can't be instantiated.
     * @since       1.1.8
     * @version     1.2.1
     * @author      Alexander Savin
     */
    internal class Utils private constructor() {
        /** Constants.Utils Class initializer block. */
        init {
            throw UnsupportedOperationException("Constants.Utils$UTILITY_CLASS_MESSAGE")
        }

        /** The singleton contains utils constants. */
        companion object {
            /** The empty String. */
            const val EMPTY_STRING = ""
            /** The empty array of Strings. */
            @JvmField val EMPTY_STRING_ARRAY = arrayOfNulls<String>(0)
            /** The part 2 of a utility class unsupported operation exception message. */
            const val UTILITY_CLASS_MESSAGE = " is a utility class and it can't be instantiated!"
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.