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

165

Как рекомендуется создавать константы в Kotlin? А что такое соглашение об именах? Я не нашел этого в документации.

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

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

Или ...?

Jodimoro
источник
4
Если вы хотите что-то, соответствующее public static finalполю в Java, используйте const valв своем объекте-компаньоне. Если вы хотите private static finalполе и публичный метод получения, используйте valв своем объекте-компаньоне.
Майкл
2
Вот запись блога, которая объясняет способы определения констант в Kotlin: blog.egorand.me/where-do-i-put-my-constants-in-kotlin
Micer
Оформить заказ этой статьи . Он дает хороший обзор различных способов хранения ваших констант с соответствующими компромиссами производительности.
firedrillsergeant
Краткий
обзор

Ответы:

132

В Kotlin, если вы хотите создать локальные константы, которые должны использоваться в классе, вы можете создать их, как показано ниже

val MY_CONSTANT = "Constants"

И если вы хотите создать публичную константу в kotlin, как public static final в java, вы можете создать ее следующим образом.

companion object{

     const val MY_CONSTANT = "Constants"

}
Aarif
источник
3
Как бы я использовал его в отдельном файле, как новый файл с именем Constants.ktили как?
Навид Аббас
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

Sudesh
источник
Разве объект-компаньон не является особым случаем объекта? Как может const valотличить объект-компаньон от объекта const valобычного объекта (т. Е. Единственное различие между вашими примерами заключается в том, что вы опускаете его constв случае объекта-компаньона - если вы добавите const, примеры должны иметь одинаковую производительность)
Эрвин Bolwidt
1
@ErwinBolwidt Я думаю, что точка зрения @ sudesh заключается в том, что не следует использовать дизайн class-wrapping-companion-object, когда единственной целью структуры является предоставление пространства имен для некоторых постоянных значений. Но если ваша структура должна быть инстанцируемой, а также содержать пару const vals, объявление a companion objectявляется правильным.
Ари Ласенски
7
@ErwinBolwidt: sudesh верно, сгенерированный байт-код для сопутствующих объектов включает в себя создание дополнительных объектов с использованием геттеров под капотом. Для хорошего объяснения с примерами декомпилированных kotlin см. Blog.egorand.me/where-do-i-put-my-constants-in-kotlin
dominik
2
спасибо @dominik, это очень подробная статья, я рекомендую ее всем, кто хочет глубоко это понять, во многих случаях kotlin генерирует субоптимальный байт-код, jetbrains устраняет множество таких ошибок, связанных с производительностью ... следите за обсуждением .kotlinlang.org , вы будете проинформированы о многих таких основных аспектах.
sudesh
1
Я многому научился из твоего ответа сегодня @sudesh спасибо!
Ракхи Дхавале
34

Прежде всего , соглашение о присвоении имен в Kotlin для констант такое же, как и в Java (например, MY_CONST_IN_UPPERCASE).

Как мне его создать?

1. В качестве значения верхнего уровня (рекомендуется)

Вы просто должны поместить свой const за пределы объявления класса.

Две возможности : объявить ваше const в файле класса (ваше const имеет четкую связь с вашим классом)

private const val CONST_USED_BY_MY_CLASS = 1

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

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

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://"
    }
}

Примечание :

В kotlin const может содержать только примитивные типы. Если вы хотите передать ему функцию, вам нужно добавить аннотацию @JvmField. Во время компиляции он будет преобразован как общедоступная статическая конечная переменная. Но это медленнее, чем с примитивным типом. Постарайся избежать этого.

@JvmField val foo = Foo()
A.Mamode
источник
это должен быть принятый ответ. в любом случае в случае, например: public static final Pattern REGEX_NOTEMPTY = Pattern.compile (". +") ????
Xan
24

Значения, известные во время компиляции, могут (и, на мой взгляд, должны) быть помечены как постоянные.

Соглашения об именах должны следовать за Java и должны быть должным образом видны при использовании из кода Java (это трудно сделать с сопутствующими объектами, но в любом случае).

Правильные объявления констант:

const val MY_CONST = "something"
const val MY_INT = 1
piotrpo
источник
3
Naming conventions should follow Java ones- Зачем?
Джодиморо
3
Kotlin обычно следует соглашениям Java по умолчанию, если не указано иное, чтобы сделать взаимодействие гладким.
zsmb13
4
Так указано в документации @Jodimoro kotlinlang.org/docs/reference/coding-conventions.html
Нил
2
@ Нейл, это не так.
Джодиморо
13
В этой связи я отправил им сказатьIf in doubt, default to the Java Coding Conventions
Neil
16

Вам не нужен класс, объект или сопутствующий объект для объявления констант в Kotlin. Вы можете просто объявить файл, содержащий все константы (например, Constants.kt или вы также можете поместить их в любой существующий файл Kotlin), и напрямую объявить константы внутри файла. Константы, известные во время компиляции, должны быть помечены 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, который будет дублирован с Constants.SCR_MAX_W и Constants.MAX_WIDTH через 2 года, потому что НИКТО прокручивает сотни / тысячи строк вниз, когда они нажимают Ctrl + пробел для автозаполнения. Серьезно: не делай этого. приводит к невозможности обслуживания
inigoD
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, тогда значение константы будет встроено. Под строкой я подразумеваю фактическое значение, используемое после его компиляции.

Итак, подведем итог, вот что компилятор сделает для вас:

//so for @JvmField:

Foo var1 = Constants.FOO;

//and for const:

Foo var1 = 479
j2emanue
источник
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)
Shomu
источник
1
как определить глобальный или статический метод?
Самед Талукдер
@SamadTalukder В Котлине будет весело sumValue (v1: Int, v2: Int): Int {return v1 + v2}
Shomu
5

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

Объявление переменной constочень похоже на использование staticключевого слова в Java.

Давайте посмотрим, как объявить переменную const в Kotlin:

const val COMMUNITY_NAME = "wiki"

И аналогичный код, написанный на Java, будет:

final static String COMMUNITY_NAME = "wiki";

Добавление к ответам выше -

@JvmField может использоваться для указания компилятору Kotlin не генерировать геттеры / сеттеры для этого свойства и выставлять его как поле.

 @JvmField
 val COMMUNITY_NAME: "Wiki"

Статические поля

Свойства Kotlin, объявленные в именованном объекте или объекте-компаньоне, будут иметь статические поля поддержки либо в этом именованном объекте, либо в классе, содержащем объект-компаньон.

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

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

Более подробная информация здесь - https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields

Anoop M
источник
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

Есть несколько способов определить константы в Kotlin,

Использование сопутствующего объекта

    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 для каждой переменной. Этот метод get не требуется вообще.

Чтобы избавиться от этих методов get , вы должны использовать const перед val, как показано ниже:

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!"
        }
    }
}
Александр Савин
источник