Constantes em Kotlin - qual é a maneira recomendada de criá-las?


165

Como é recomendado criar constantes no Kotlin? E qual é a convenção de nomenclatura? Eu não encontrei isso na documentação.

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

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

Ou ...?


4
Se você deseja algo correspondente a um public static finalcampo em Java, use const valem seu objeto complementar. Se você deseja um private static finalcampo e um getter público, use valem seu objeto complementar.
Michael

2
Aqui está o blogpost que explica maneiras de definir constantes em Kotlin: blog.egorand.me/where-do-i-put-my-constants-in-kotlin
Micer

Confira este artigo . Ele fornece uma boa visão geral de diferentes maneiras pelas quais você pode armazenar suas constantes, com trade-offs de desempenho relacionados.
Firedrillsergeant

Respostas:


132

No Kotlin, se você deseja criar as constantes locais que devem ser usadas na classe, crie-as como abaixo

val MY_CONSTANT = "Constants"

E se você deseja criar uma constante pública no kotlin como public static final em java, você pode criá-la da seguinte maneira.

companion object{

     const val MY_CONSTANT = "Constants"

}

3
Como eu o usaria em um arquivo separado, como um novo arquivo chamado Constants.ktou como?
Naveed Abbas

2
Eu uso um arquivo para constantes. mantenha todas as minhas constantes lá.
Filthy_wizard 17/07/19

2
você não precisa o companion objectque eu acho @piotrpo resposta deve ser o único aceite
Chiara

@Chiara, o objeto complementar (e sua classe anexa) serve como um espaço para nome, em oposição às declarações de nível superior. Eu acho que as duas respostas podem fazer sentido, dependendo da situação.
jingx

@ jingx sim, você tem um ponto lá para adicionar um espaço para nome que você precisa. : +1:
Chiara

118

Evite usar objetos complementares. Por trás do capô, os métodos de instância getter e setter são criados para que os campos sejam acessíveis. Chamar métodos de instância é tecnicamente mais caro do que chamar métodos estáticos.

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

Em vez disso, defina as constantes em object.

Prática recomendada :

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

e acesse-os globalmente assim: DbConstants.TABLE_USER_ATTRIBUTE_EMPID


Um objeto complementar não é um caso especial de um objeto? Como um const valobjeto complementar em um objeto complementar pode ser diferente de um const valobjeto comum (ou seja, a única diferença entre seus exemplos parece ser que você omitiu constno caso do objeto complementar - se você adicionar const, os exemplos devem ter o mesmo desempenho)
Erwin Bolwidt 03/02/19

1
@ErwinBolwidt Acho que o argumento de @ sudesh é que não se deve usar o design de objeto de acompanhamento de classe quando o único objetivo da estrutura é fornecer um espaço de nome para alguns valores constantes. Mas se sua estrutura precisa ser instanciada e também incluir alguns const vals, declarar a companion objectestá correto.
Ari Lacenski 06/02/19

7
@ErwinBolwidt: sudesh está certo, gerado pelo código para objetos complementares envolve a criação de objetos adicionais com getters sob o capô. Para uma boa explicação com exemplos KOTLIN descompilados ver blog.egorand.me/where-do-i-put-my-constants-in-kotlin
Dominik

2
obrigado @dominik, este é um artigo muito detalhado, eu recomendo isso para todos que querem entender isso profundamente, há muitos casos em que o kotlin produz bytecode abaixo do ideal, os jetbrains resolveram muitos desses erros relacionados ao desempenho ... fique de olho em discutir .kotlinlang.org , você será informado sobre muitos desses aspectos subjacentes.
sudesh 13/03/19

1
Aprendi muito com a sua resposta hoje @sudesh obrigado!
Rakhi Dhavale

34

Primeiro , a convenção de nomenclatura no Kotlin para constantes é a mesma que no java (por exemplo: MY_CONST_IN_UPPERCASE).

Como devo criá-lo?

1. Como um valor de nível superior (recomendado)

Você apenas precisa colocar sua const fora da declaração de classe.

Duas possibilidades : declarar sua const no seu arquivo de classe (sua const tem uma relação clara com sua classe)

private const val CONST_USED_BY_MY_CLASS = 1

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

Crie um arquivo constants.kt dedicado onde armazenar essas const globais (aqui você deseja usar sua const amplamente em todo o projeto):

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

Então você só precisa importá-lo onde precisar:

import com.project.constants

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

2. Declare-o em um objeto complementar (ou em uma declaração de objeto)

Isso é muito menos limpo porque, sob o capô, quando o bytecode é gerado, um objeto inútil é criado:

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
    }
}

Pior ainda, se você o declarar como um val em vez de uma const (o compilador gerará um objeto inútil + uma função inútil):

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

Nota :

No kotlin, const pode conter apenas tipos primitivos. Se você deseja passar uma função para ela, é necessário adicionar a anotação @JvmField. No momento da compilação, ele será transformado como uma variável final estática pública. Mas é mais lento do que com um tipo primitivo. Tente evitá-lo.

@JvmField val foo = Foo()

essa deve ser a resposta aceita. mesmo assim em um caso como: public static final Pattern REGEX_NOTEMPTY = Pattern.compile (". +") ????
Xan

24

Valores conhecidos em tempo de compilação podem (e na minha opinião deveriam) ser marcados como constantes.

As convenções de nomenclatura devem seguir as Java e devem estar visíveis quando usadas a partir do código Java (é algo difícil de ser alcançado com objetos complementares, mas de qualquer maneira).

As declarações constantes apropriadas são:

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

3
Naming conventions should follow Java ones- porque?
Jodimoro 18/05

3
O Kotlin geralmente segue as convenções Java por padrão, se não especificado de outra forma, para facilitar a interoperabilidade.
Zsmb13 18/05

4
É especificado assim na documentação @Jodimoro kotlinlang.org/docs/reference/coding-conventions.html
Neil

2
@ Neil, não é.
Jodimoro

13
Nesse link eu postei eles dizemIf in doubt, default to the Java Coding Conventions
Neil

16

Você não precisa de uma classe, um objeto ou um objeto complementar para declarar constantes no Kotlin. Você pode simplesmente declarar um arquivo contendo todas as constantes (por exemplo, Constants.kt ou também pode colocá-las dentro de qualquer arquivo Kotlin existente) e declarar diretamente as constantes dentro do arquivo. As constantes conhecidas em tempo de compilação devem ser marcadas com const.

Portanto, neste caso, deve ser:

const val MY_CONST = "something"

e então você pode importar a constante usando:

import package_name.MY_CONST

Você pode consultar este link


13
As constantes devem estar na classe com a qual estão relacionadas. Se você criar uma classe 'Constantes', terminará, eventualmente, centenas de constantes dentro dela. Pe: MAX_WIDTH, MAX_HEIGHT deve estar na classe Screen para que você possa acessá-lo logicamente: Screen.MAX_WIDTH e não precisará colocar Constants.SCREEN_MAX_WIDTH que será duplicado com Constants.SCR_MAX_W e Constants.MAX_WIDTH em 2 anos porque NOBODY rola centenas / milhares de linhas para baixo quando pressionam Ctrl + espaço para o preenchimento automático. Sério: não faça isso. leva à
falta de

1
@inigoD Isso é verdade se você usar a constante em um só lugar ou apenas em crianças, mas esse nunca é o caso. Se você colocar a constante em uma classe obscura, esquecê-la ou, mais provavelmente, assumir uma base de código, poderá duplicá-la. Ou não é óbvio onde colocá-los. A fonte ou o destino? Você pode criar vários arquivos constantes, fáceis de encontrar. Um para chaves de preferências, uma para teclas de solicitação, um para constantes de vista, etc.
Herrbert74

1
@ Herrbert74 Sinto muito, mas tenho que discordar de você. Concordo que às vezes pode ser difícil encontrar qual é, mas um local constante deve sempre ser a classe que está mais relacionada a ele. E salvá-los aleatoriamente em um número aleatório de arquivos não é a melhor maneira, se você deseja recuperá-los mais tarde ... Você argumentará que eles não seriam armazenados aleatoriamente, mas nos pacotes às quais as constantes estão relacionadas, mas isso é apenas uma desculpa para não colocá-los nas classes com as quais eles estão relacionados, que é, no final, o seu lugar ...
inigoD

4
Se uma constante é verdadeiramente global ou tem um amplo escopo ... como um valor para uma anotação usada em todos os pacotes ou um nome de Cabeçalho que está sendo buscado por vários Controladores, etc, é completamente aceitável criar "constantes" classe "com escopo adequado. No entanto, constantes que são usadas apenas em contextos específicos, devem ter escopo definido para esse contexto e declaradas na classe relevante.
Nephthys76

@ Nephthys76 Apenas como uma observação, especificamente para " como um valor para uma anotação usada em todos os pacotes ", eu diria que o melhor local para a constante é na classe de anotação.
Slaw

8

Se você colocar o seu const val valName = valValueantes do nome da classe, dessa maneira ele criará um

public static final YourClass.Ktque terão os public static finalvalores.

Kotlin :

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

Java descompilado:

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

Isso é verdade? Alguém tem alguma experiência com este método?
Scott Biggs

5
class Myclass {

 companion object {
        const val MYCONSTANT = 479
}

você tem duas opções: pode usar a constpalavra-chave ou a @JvmFieldque faz dela uma constante final estática de java.

class Myclass {

     companion object {
           @JvmField val MYCONSTANT = 479
    }

Se você usar a @JvmFieldanotação, depois que ela compilar, a constante será inserida para você da maneira que você chamaria em java.
Assim como você chamaria em java, o compilador substituirá isso quando você chamar a constante complementar no código.

No entanto, se você usar a palavra-chave const, o valor da constante será incorporado. Por inline, quero dizer que o valor real é usado após a compilação.

Então, para resumir aqui, é o que o compilador fará por você:

//so for @JvmField:

Foo var1 = Constants.FOO;

//and for const:

Foo var1 = 479

5

O valor estático e constante de Kotlin e o método declaram

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
}

}

Acesse valor em qualquer lugar

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

1
como definir método global ou estático?
Samad Talukder

@SamadTalukder No Kotlin, será divertido sumValue (v1: Int, v2: Int): Int {return v1 + v2}
Shomu

5

Assim val, variáveis ​​definidas com a constpalavra - chave são imutáveis. A diferença aqui é que ela consté usada para variáveis ​​conhecidas em tempo de compilação.

Declarar uma variável consté muito parecido com o uso da staticpalavra - chave em Java.

Vamos ver como declarar uma variável const no Kotlin:

const val COMMUNITY_NAME = "wiki"

E o código análogo escrito em Java seria:

final static String COMMUNITY_NAME = "wiki";

Adicionando às respostas acima -

@JvmField pode ser usado para instruir o compilador Kotlin a não gerar getters / setters para essa propriedade e expô-lo como um campo.

 @JvmField
 val COMMUNITY_NAME: "Wiki"

Campos estáticos

As propriedades Kotlin declaradas em um objeto nomeado ou em um objeto complementar terão campos de suporte estáticos nesse objeto nomeado ou na classe que contém o objeto complementar.

Geralmente esses campos são particulares, mas podem ser expostos de uma das seguintes maneiras:

  • @JvmField anotação;
  • lateinit modificador;
  • const modificador.

Mais detalhes aqui - https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields


4

Algo que não é mencionado em nenhuma das respostas é a sobrecarga de uso companion objects. Como você pode ler aqui , os objetos complementares são de fato objetos e a criação deles consome recursos. Além disso, pode ser necessário executar mais de uma função getter toda vez que você usa sua constante. Se tudo o que você precisa são algumas constantes primitivas, provavelmente será melhor usá-lo valpara obter um melhor desempenho e evitar o companion object.

TL; DR; do artigo:

O uso do objeto complementar realmente transforma esse código

class MyClass {

    companion object {
        private val TAG = "TAG"
    }

    fun helloWorld() {
        println(TAG)
    }
}

Nesse código:

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));
    }
}

Portanto, tente evitá-los.


3

constantes locais:

const val NAME = "name"

Constantes globais:

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

acessar MyConstants.NAME


1

Existem algumas maneiras de definir constantes no Kotlin,

Usando objeto complementar

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

você pode usar o bloco de objetos complementares acima em qualquer classe e definir todos os seus campos dentro desse próprio bloco. Mas há um problema com essa abordagem, a documentação diz:

mesmo que os membros de objetos complementares pareçam membros estáticos em outros idiomas, em tempo de execução, eles ainda são membros de instância de objetos reais e podem, por exemplo, implementar interfaces.

Quando você cria suas constantes usando o objeto complementar e vê o código de código descompilado , verá algo como abaixo,

  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;
     }
  }

A partir daqui, você pode ver facilmente o que a documentação dizia, mesmo que os membros de objetos complementares pareçam membros estáticos em outros idiomas, em tempo de execução esses ainda são membros de instância de objetos reais. Está fazendo um trabalho extra do que o necessário.

Agora vem outra maneira, na qual não precisamos usar objetos complementares, como abaixo,

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

Novamente, se você vir a versão descompilada do código de bytes do snippet acima, encontrará algo parecido com isto,

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";
      }
    }

Agora, se você vir o código descompilado acima, está criando o método get para cada variável. Este método get não é necessário.

Para se livrar desses métodos get , você deve usar const before val como abaixo,

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

Agora, se você vir o código descompilado do snippet acima, achará mais fácil ler, pois faz a menor conversão em segundo plano para o seu código.

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

    private ApiConstants() {
     }

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

Portanto, esta é a melhor maneira de criar constantes.


0

Para primitivas e Strings:

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

Para outros casos:

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

Exemplo:

/*
 * 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!"
        }
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.