android - new - Constantes en Kotlin: ¿cuál es una forma recomendada de crearlos?
new object in kotlin (11)
¿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 ...?
Algo que no se menciona en ninguna de las respuestas es la sobrecarga del uso de
companion objects
.
Como puede leer
here
, los objetos complementarios son de hecho objetos y su creación consume recursos.
Además, es posible que deba pasar por más de una función getter cada vez que use su constante.
Si todo lo que necesita son unas constantes primitivas, probablemente sea mejor que use
val
para obtener un
mejor rendimiento
y evitar el
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.
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"
}
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 el
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
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 otra forma, 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;
}
}
Así que esta es la mejor manera de crear constantes.
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 apropiada 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
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
) 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 link
Si coloca su
const val valName = valValue
antes del nombre de la clase, de esta manera creará un
public static final YourClass.Kt
que tendrá los valores
public static final
.
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
constantes locales:
const val NAME = "name"
Constantes globales:
object MyConstants{
val NAME = "name"
val ID = "_id"
var EMAIL = "email"
}
acceder a MyConstants.NAME
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()
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!"
}
}
}
class Myclass {
companion object {
const val MYCONSTANT = 479
}
tiene dos opciones: puede usar la palabra clave
const
o usar
@JvmField
que la convierte en una constante final estática de java.
class Myclass {
companion object {
@JvmField val MYCONSTANT = 479
}
Si usa la anotación
@JvmField
, luego de que se compila, la constante se coloca 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.
así que para resumir aquí es lo que el compilador hará por usted:
//so for @JvmField:
Foo var1 = Constants.FOO;
//and for const:
Foo var1 = 479