Constantes en Kotlin: ¿cuál es una forma recomendada de crearlos?

165

¿Cómo se recomienda crear constantes en Kotlin? ¿Y cuál es la convención de nomenclatura? No he encontrado eso en la documentación.

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

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

O ...

Jodimoro
fuente
44
Si desea algo correspondiente a un public static finalcampo en Java, utilícelo const valen su objeto complementario. Si desea un private static finalcampo y un captador público, úselo valen su objeto complementario.
Michael
2
Aquí está la publicación de blog
Micer
Mira este artículo . Ofrece una buena descripción de las diferentes formas en que puede almacenar sus constantes, con compensaciones de rendimiento relacionadas.
firedrillsergeant

Respuestas:

132

En Kotlin, si desea crear las constantes locales con las que se supone que deben usarse en la clase, puede crearlas como se muestra a continuación.

val MY_CONSTANT = "Constants"

Y si desea crear una constante pública en kotlin como public static final en java, puede crearla de la siguiente manera.

companion object{

     const val MY_CONSTANT = "Constants"

}
AaRiF
fuente
3
¿Cómo lo usaría en un archivo separado como un nuevo archivo llamado Constants.kto cómo?
Naveed Abbas
2
Yo uso un archivo para constantes. mantén todas mis constantes allí.
filthy_wizard
2
no necesita la companion objectrespuesta Creo que @piotrpo debería ser la aceptada
Chiara
@Chiara, el objeto complementario (y su clase adjunta) sirve como un espacio de nombres, a diferencia de las declaraciones de nivel superior. Creo que ambas respuestas pueden tener sentido dependiendo de la situación.
jingx
@jingx sí, tiene un punto allí para agregarle un espacio de nombres que lo necesita. : +1:
Chiara
118

Evite usar objetos de compañía. Detrás del capó, se crean métodos de instancia getter y setter para que los campos sean accesibles. Llamar a métodos de instancia es técnicamente más costoso que llamar a métodos estáticos.

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

En su lugar, defina las constantes en object.

Práctica recomendada :

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

y acceder a ellos globalmente de esta manera: DbConstants.TABLE_USER_ATTRIBUTE_EMPID

sudesh
fuente
¿No es un objeto compañero un caso especial de un objeto? ¿Cómo puede un const valobjeto complementario ser diferente de un const valobjeto común (es decir, la única diferencia entre sus ejemplos parece ser que omitió consten el caso del objeto complementario? Si agrega const, los ejemplos deberían tener el mismo rendimiento)
Erwin Bolwidt
1
@ErwinBolwidt Creo que el punto de @ sudesh es que no se debe usar el diseño de objeto de envoltura de clase cuando el único propósito de la estructura es proporcionar un espacio de nombres para algunos valores constantes. Pero si su estructura necesita ser instanciable y también incluir un par de const vals, declarar a companion objectes correcto.
Ari Lacenski
77
@ErwinBolwidt: sudesh tiene razón, el código de bytes generado para los objetos complementarios implica la creación de objetos adicionales con captadores bajo el capó. Para una buena explicación con ejemplos de Kotlin descompilados, vea blog.egorand.me/where-do-i-put-my-constants-in-kotlin
dominik
2
gracias @dominik, este es un artículo muy detallado, lo recomiendo a todo el que quiera entender esto en profundidad, hay muchos casos en los que Kotlin produce código de bytes subóptima, JetBrains han resuelto muchos errores relacionados tales rendimiento ... mantener un ojo en discutir .kotlinlang.org , se le informará sobre muchos de estos aspectos subyacentes.
sudesh
1
He aprendido mucho de su respuesta hoy @sudesh gracias!
Rakhi Dhavale
34

En primer lugar , la convención de nomenclatura en Kotlin para constantes es la misma que en Java (por ejemplo: MY_CONST_IN_UPPERCASE).

¿Cómo debo crearlo?

1. Como valor de nivel superior (recomendado)

Solo tiene que poner su constante fuera de su declaración de clase.

Dos posibilidades : declara tu constante en tu archivo de clase (tu constante tiene una relación clara con tu clase)

private const val CONST_USED_BY_MY_CLASS = 1

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

Cree un archivo dedicado constants.kt donde almacenar esas constantes globales (aquí desea utilizar su constante en todo su proyecto):

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

Luego solo tiene que importarlo donde lo necesite:

import com.project.constants

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

2. Declararlo en un objeto complementario (o una declaración de objeto)

Esto es mucho menos limpio porque debajo del capó, cuando se genera el código de bytes, se crea un objeto inútil:

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

Peor aún si lo declaras como un val en lugar de un const (el compilador generará un objeto inútil + una función inútil):

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

Nota :

En kotlin, const solo puede contener tipos primitivos. Si desea pasarle una función, debe agregar la anotación @JvmField. En tiempo de compilación, se transformará como una variable final estática pública. Pero es más lento que con un tipo primitivo. Intenta evitarlo.

@JvmField val foo = Foo()
A.Mamode
fuente
Esta debería ser la respuesta aceptada. de todos modos en un caso como: public static final Pattern REGEX_NOTEMPTY = Pattern.compile (". +") ????
Xan
24

Los valores conocidos en tiempo de compilación pueden (y en mi opinión deberían) marcarse como constantes.

Las convenciones de nomenclatura deben seguir a las de Java y deben ser visibles de manera adecuada cuando se usan desde el código de Java (de alguna manera es difícil de lograr con objetos complementarios, pero de todos modos).

Las declaraciones constantes adecuadas son:

const val MY_CONST = "something"
const val MY_INT = 1
piotrpo
fuente
3
Naming conventions should follow Java ones- ¿por qué?
Jodimoro
3
Kotlin generalmente sigue las convenciones de Java de forma predeterminada, si no se especifica lo contrario, para facilitar la interoperabilidad.
zsmb13
44
Se especifica así en la documentación @Jodimoro kotlinlang.org/docs/reference/coding-conventions.html
Neil
2
@Neil, no lo es.
Jodimoro
13
En ese enlace que publiqué dicenIf in doubt, default to the Java Coding Conventions
Neil
16

No necesita una clase, un objeto o un objeto complementario para declarar constantes en Kotlin. Puede declarar un archivo que contenga todas las constantes (por ejemplo, Constants.kt o también puede colocarlas dentro de cualquier archivo Kotlin existente) y declarar directamente las constantes dentro del archivo. Las constantes conocidas en tiempo de compilación deben marcarse con const.

Entonces, en este caso, debería ser:

const val MY_CONST = "something"

y luego puedes importar la constante usando:

import package_name.MY_CONST

Puedes consultar este enlace

Abdul Wadood
fuente
13
Las constantes deben estar en la clase con la que están relacionadas. Si haces una clase de 'Constantes' terminarás, eventualmente, habrá cientos de constantes dentro de ella. Pe: MAX_WIDTH, MAX_HEIGHT debe estar en la clase Screen para que pueda acceder a él lógicamente: Screen.MAX_WIDTH y no necesita poner Constants.SCREEN_MAX_WIDTH que se duplicará con Constants.SCR_MAX_W y Constants.MAX_WIDTH en 2 años porque NADIE desplaza cientos / miles de líneas hacia abajo cuando presionan Ctrl + espacio para autocompletar. En serio: no lo hagas. conduce a la falta de mantenimiento
inigoD
1
@inigoD Eso es cierto si usa la constante en un lugar o solo en niños, pero este casi nunca es el caso. Si coloca la constante en una clase oscura, entonces se olvida de ella o es más probable que se haga cargo de una base de código, podría duplicarla. O no es obvio dónde ponerlos. ¿El origen o el destino? Puede crear varios archivos constantes, que son fáciles de encontrar. Una para las claves de preferencia, una para las claves de solicitud, otra para las constantes de visualización, etc.
Herrbert74
1
@ Herrbert74 Lo siento, pero tengo que estar en desacuerdo con usted. Estoy de acuerdo en que a veces puede ser difícil encontrar cuál es, pero un lugar constante siempre debe ser la clase que está más relacionada con ella. Y guardarlos aleatoriamente en archivos de números aleatorios no es la mejor manera si desea recuperarlos más tarde ... Argumentará que no se almacenarían aleatoriamente sino en paquetes con los que están relacionadas las constantes, pero eso es solo una excusa para no ponerlos en las clases con las que están relacionados, que es, al final, su lugar ...
inigoD
44
Si una constante es verdaderamente global o tiene un gran alcance ... como un valor para una anotación utilizada en todos los paquetes, o un nombre de encabezado que está siendo recuperado por múltiples controladores, etc., entonces es completamente aceptable crear una "constante clase "que tiene un alcance apropiado. Sin embargo, las constantes que solo se usan en contextos específicos, deben tener un alcance en ese contexto y declararse en la clase relevante.
Nephthys76
@ Nephthys76 Solo como una nota, para " como un valor para una anotación utilizada en todos los paquetes " específicamente, yo diría que el mejor lugar para la constante es en la clase de anotación.
Slaw
8

Si pones tu const val valName = valValueantes del nombre de la clase, de esta forma creará un

public static final YourClass.Kteso tendrá los 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
Thales Pupo Araujo
fuente
¿Es esto cierto? ¿Alguien tiene alguna experiencia con este método?
Scott Biggs
5
class Myclass {

 companion object {
        const val MYCONSTANT = 479
}

tiene dos opciones: puede usar la constpalabra clave o usar la @JvmFieldque la convierte en una constante final estática de java.

class Myclass {

     companion object {
           @JvmField val MYCONSTANT = 479
    }

Si usa la @JvmFieldanotación, luego de que se compila, la constante se pone para usted de la forma en que la llamaría en java.
Al igual que lo llamarías en Java, el compilador lo reemplazará por ti cuando llames a la constante compañera en el código.

Sin embargo, si usa la palabra clave const, entonces el valor de la constante se alinea. Por en línea quiero decir que el valor real se usa después de compilar.

para resumir aquí es lo que el compilador hará por usted:

//so for @JvmField:

Foo var1 = Constants.FOO;

//and for const:

Foo var1 = 479
j2emanue
fuente
5

El valor y método estático y constante de Kotlin declaran

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
}

}

Acceda al valor desde cualquier lugar

val value = MyConstant.PI
val value = MyConstant.sumValue(10,5)
Shomu
fuente
1
¿Cómo definir el método global o estático?
Samad Talukder
@SamadTalukder En Kotlin será divertido sumValue (v1: Int, v2: Int): Int {return v1 + v2}
Shomu
5

Al igual val, las variables definidas con la constpalabra clave son inmutables. La diferencia aquí es que constse usa para variables que se conocen en tiempo de compilación.

Declarar una variable constes muy parecido a usar la staticpalabra clave en Java.

Veamos cómo declarar una variable const en Kotlin:

const val COMMUNITY_NAME = "wiki"

Y el código análogo escrito en Java sería:

final static String COMMUNITY_NAME = "wiki";

Agregando a las respuestas anteriores -

@JvmField puede usarse para indicar al compilador de Kotlin que no genere captadores / establecedores para esta propiedad y exponerlo como un campo.

 @JvmField
 val COMMUNITY_NAME: "Wiki"

Campos estáticos

Las propiedades de Kotlin declaradas en un objeto nombrado o un objeto complementario tendrán campos de respaldo estáticos en ese objeto nombrado o en la clase que contiene el objeto complementario.

Por lo general, estos campos son privados, pero pueden exponerse de una de las siguientes maneras:

  • @JvmField anotación;
  • lateinit modificador
  • const modificador

Más detalles aquí: https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields

Anoop M
fuente
4

Algo que no se menciona en ninguna de las respuestas es la sobrecarga de uso companion objects. Como puede leer aquí , los objetos complementarios son de hecho objetos y su creación consume recursos. Además, es posible que deba realizar más de una función getter cada vez que use su constante. Si todo lo que necesita son unas pocas constantes primitivas, probablemente sea mejor usarlas valpara obtener un mejor rendimiento y evitarlas companion object.

TL; DR; del artículo:

El uso de objetos complementarios en realidad convierte este código

class MyClass {

    companion object {
        private val TAG = "TAG"
    }

    fun helloWorld() {
        println(TAG)
    }
}

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

Así que trata de evitarlos.

Sir Codesalot
fuente
3

constantes locales:

const val NAME = "name"

Constantes globales:

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

acceder a MyConstants.NAME

Amjed Baig
fuente
1

Hay algunas formas de definir constantes en Kotlin,

Usando objeto complementario

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

puede usar el bloque de objeto complementario anterior dentro de cualquier clase y definir todos sus campos dentro de este bloque. Pero hay un problema con este enfoque, la documentación dice:

aunque los miembros de los objetos complementarios se parecen a los miembros estáticos en otros idiomas, en el tiempo de ejecución siguen siendo miembros de objetos reales y, por ejemplo, pueden implementar interfaces.

Cuando cree sus constantes utilizando un objeto complementario y vea el código de bytes descompilado , verá algo como a continuación,

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

Desde aquí puede ver fácilmente lo que dice la documentación, aunque los miembros de los objetos complementarios se vean como miembros estáticos en otros idiomas, en el tiempo de ejecución siguen siendo miembros de objetos reales. Está haciendo un trabajo adicional al requerido.

Ahora viene de otra manera, donde no necesitamos usar objetos complementarios como a continuación,

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

Nuevamente, si ve la versión descompilada del código de bytes del fragmento anterior, encontrará algo como esto,

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

Ahora, si ve el código descompilado anterior, está creando el método get para cada variable. Este método get no es obligatorio en absoluto.

Para deshacerse de estos métodos get , debe usar const antes de val como se muestra a continuación,

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

Ahora, si ve el código descompilado del fragmento anterior, le resultará más fácil de leer, ya que hace la menor conversión de fondo para su 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;
      }
    }

Entonces esta es la mejor manera de crear constantes.

Abhishek Kumar
fuente
0

Para primitivas y cadenas:

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

Para otros casos:

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

Ejemplo:

/*
 * 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!"
        }
    }
}
Alexander Savin
fuente