Costanti a Kotlin: qual è il modo consigliato per crearli?


165

Come si consiglia di creare costanti in Kotlin? E qual è la convenzione di denominazione? Non l'ho trovato nella documentazione.

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

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

O ...?


4
Se si desidera qualcosa corrispondente a un public static finalcampo in Java, utilizzare const valnell'oggetto compagno. Se si desidera un private static finalcampo e un getter pubblico, utilizzare valnell'oggetto compagno.
Michael,

2
Ecco il blogpost che spiega i modi per definire costanti in Kotlin: blog.egorand.me/where-do-i-put-my-constants-in-kotlin
Micer

Dai un'occhiata a questo articolo . Offre una buona panoramica dei diversi modi in cui è possibile memorizzare le costanti, con relativi compromessi delle prestazioni.
firedrillsergeant

Risposte:


132

In Kotlin, se vuoi creare le costanti locali che dovrebbero essere usate nella classe, puoi crearle come di seguito

val MY_CONSTANT = "Constants"

E se vuoi creare una costante pubblica in kotlin come final statica pubblica in java, puoi crearla come segue.

companion object{

     const val MY_CONSTANT = "Constants"

}

3
Come lo userei in un file separato come un nuovo file chiamato Constants.kto come?
Naveed Abbas,

2
io uso un file per le costanti. tieni tutte le mie costanti lì dentro.
filthy_wizard,

2
non hai bisogno della companion objectrisposta credo che @piotrpo dovrebbe essere quella accettata
Chiara

@Chiara l'oggetto compagno (e la sua classe che lo racchiude) funge da spazio dei nomi, al contrario delle dichiarazioni di livello superiore. Penso che entrambe le risposte possano avere senso a seconda della situazione.
jingx,

@jingx sì, hai un punto lì per aggiungere uno spazio dei nomi ad esso ti serve. : +1:
Chiara il

118

Evitare l'uso di oggetti associati. Dietro al cofano vengono creati metodi di istanza getter e setter per rendere accessibili i campi. Chiamare metodi di istanza è tecnicamente più costoso di chiamare metodi statici.

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

Definisci invece le costanti in object.

Pratica raccomandata :

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

e accedervi a livello globale in questo modo: DbConstants.TABLE_USER_ATTRIBUTE_EMPID


Un oggetto compagno non è un caso speciale di un oggetto? Come può un const valoggetto in un compagno essere diverso da un const valin un oggetto ordinario (cioè l'unica differenza tra i tuoi esempi sembra essere quella che hai omesso constnel caso dell'oggetto compagno - se aggiungi const, gli esempi dovrebbero avere le stesse prestazioni)
Erwin Bolwidt,

1
@ErwinBolwidt Penso che il punto di @ sudesh sia che non si dovrebbe usare il design di oggetti compagno di classe quando il solo scopo della struttura è fornire uno spazio dei nomi per alcuni valori costanti. Ma se la tua struttura deve essere istantanea e racchiudere anche un paio di const vals, dichiarare che companion objectè corretta.
Ari Lacenski,

7
@ErwinBolwidt: sudesh ha ragione, generato dal codice per oggetti associati comporta la creazione di oggetti aggiuntivi con getter sotto il cofano. Per una buona spiegazione con esempi di kotlin decompilati consultare blog.egorand.me/where-do-i-put-my-constants-in-kotlin
dominik

2
grazie @dominik, questo è un articolo molto dettagliato, vi consiglio questo per tutti coloro che vogliono capire questo in profondità, ci sono molti casi in cui Kotlin produce bytecode non ottimale, JetBrains hanno risolto molti bug tali prestazioni legate ... tenere d'occhio discutere .kotlinlang.org , sarai informato su molti di questi aspetti sottostanti.
sudesh,

1
Ho imparato molto dalla tua risposta oggi @sudesh grazie!
Rakhi Dhavale,

34

Prima di tutto , la convenzione di denominazione in Kotlin per le costanti è la stessa di java (ad esempio: MY_CONST_IN_UPPERCASE).

Come dovrei crearlo?

1. Come valore di livello superiore (consigliato)

Devi solo mettere la tua const fuori dalla dichiarazione di classe.

Due possibilità : dichiara la tua const nel tuo file di classe (la tua const ha una chiara relazione con la tua classe)

private const val CONST_USED_BY_MY_CLASS = 1

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

Crea un file constants.kt dedicato dove archiviare quelle const globali (qui vuoi usare le tue const ampiamente nel tuo progetto):

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

Quindi devi solo importarlo dove ti serve:

import com.project.constants

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

2. Dichiaralo in un oggetto associato (o in una dichiarazione di oggetto)

Questo è molto meno pulito perché sotto il cofano, quando viene generato il bytecode, viene creato un oggetto inutile:

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

Ancora peggio se lo dichiarate come val anziché come const (il compilatore genererà un oggetto inutile + una funzione inutile):

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

Nota :

In kotlin, const può contenere solo tipi primitivi. Se si desidera passare una funzione ad essa, è necessario aggiungere l'annotazione @JvmField. Al momento della compilazione, verrà trasformato come variabile finale statica pubblica. Ma è più lento che con un tipo primitivo. Cerca di evitarlo.

@JvmField val foo = Foo()

questa dovrebbe essere la risposta accettata. comunque in un caso come: modello statico pubblico finale REGEX_NOTEMPTY = Pattern.compile (". +") ????
Xan,

24

I valori noti al momento della compilazione possono (e secondo me dovrebbero) essere contrassegnati come costanti.

Le convenzioni di denominazione dovrebbero seguire quelle di Java e dovrebbero essere adeguatamente visibili se utilizzate dal codice Java (è in qualche modo difficile da ottenere con oggetti associati, ma comunque).

Le dichiarazioni costanti appropriate sono:

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

3
Naming conventions should follow Java ones- perché?
Jodimoro,

3
Kotlin di solito segue le convenzioni Java per impostazione predefinita, se non diversamente specificato, per rendere l'interoperabilità regolare.
zsmb13,

4
È specificato come quello nella documentazione @Jodimoro kotlinlang.org/docs/reference/coding-conventions.html
Neil

2
@Neil, non lo è.
Jodimoro,

13
In quel link che ho pubblicato diconoIf in doubt, default to the Java Coding Conventions
Neil,

16

Non hai bisogno di una classe, un oggetto o un oggetto compagno per dichiarare le costanti in Kotlin. Puoi semplicemente dichiarare un file contenente tutte le costanti (ad esempio Constants.kt oppure puoi anche metterle all'interno di qualsiasi file Kotlin esistente) e dichiarare direttamente le costanti all'interno del file. Le costanti note al momento della compilazione devono essere contrassegnate con const.

Quindi, in questo caso, dovrebbe essere:

const val MY_CONST = "something"

e quindi puoi importare la costante usando:

import package_name.MY_CONST

Puoi fare riferimento a questo link


13
Le costanti devono appartenere alla classe a cui sono legate. Se fai una classe "Costanti" finirai, eventualmente, con centinaia di costanti al suo interno. Pe: MAX_WIDTH, MAX_HEIGHT devono essere nella classe Screen in modo che tu possa accedervi logicamente: Screen.MAX_WIDTH e non è necessario inserire Costanti.SCREEN_MAX_WIDTH che saranno duplicati con Costanti.SCR_MAX_W e Costanti.MAX_WIDTH tra 2 anni perché NESSUNO scorre le righe di centinaia / migliaia di volte verso il basso quando premono Ctrl + spazio per completare automaticamente. Scherzi a parte: non farlo. porta alla non
mantenibilità

1
@inigoD È vero se usi la costante in un posto o solo nei bambini, ma non è quasi mai così. Se si inserisce la costante in una classe oscura, se ne dimentica o più probabilmente si acquisisce una base di codice, è possibile duplicarla. O non è ovvio dove metterli. La fonte o la destinazione? Puoi creare diversi file costanti, facili da trovare. Uno per le chiavi di preferenza, uno per le chiavi di richiesta, uno per le costanti di visualizzazione, ecc.
Herrbert74,

1
@ Herrbert74 Mi dispiace ma non sono d'accordo con te. Sono d'accordo sul fatto che a volte può essere difficile trovare quale sia, ma un posto costante dovrebbe sempre essere la classe che è più legata ad essa. E salvarli in modo casuale in file di numeri casuali non è il modo migliore se desideri recuperarli in seguito ... Sosterrai che non verrebbero archiviati in modo casuale ma in pacchetti a cui sono collegate le costanti, ma questa è solo una scusa per non metterli nelle classi a cui sono legati, che è, alla fine, il loro posto ...
inigoD

4
Se una costante è veramente globale o ha un ampio ambito ... come un valore per un'annotazione utilizzata in tutti i pacchetti o un nome di intestazione che viene recuperato da più controller, ecc., È del tutto accettabile creare una "costante classe "che ha un ambito appropriato. Tuttavia, le costanti che vengono utilizzate solo in contesti specifici, devono essere incluse in tale contesto e dichiarate nella relativa classe.
Nephthys76,

@ Nephthys76 Proprio come una nota, per " come un valore per un'annotazione utilizzata in tutti i pacchetti ", direi che il posto migliore per la costante è nella classe di annotazione.
Slaw

8

Se metti il ​​tuo const val valName = valValueprima del nome della classe, in questo modo creerà un

public static final YourClass.Ktche avrà i public static finalvalori.

Kotlin :

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

Java decompilato:

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

È vero? Qualcuno ha qualche esperienza con questo metodo?
Scott Biggs,

5
class Myclass {

 companion object {
        const val MYCONSTANT = 479
}

puoi scegliere tra due constparole chiave o utilizzare la @JvmFieldche la rende una costante finale statica di Java.

class Myclass {

     companion object {
           @JvmField val MYCONSTANT = 479
    }

Se usi l' @JvmFieldannotazione, dopo che è stata compilata la costante viene inserita per te come la chiameresti in java.
Proprio come lo chiameresti in Java, il compilatore lo sostituirà quando chiami la costante compagno nel codice.

Tuttavia, se si utilizza la parola chiave const, il valore della costante viene sottolineato. Per inline intendo che il valore effettivo viene utilizzato dopo la compilazione.

quindi per riassumere ecco cosa farà il compilatore per te:

//so for @JvmField:

Foo var1 = Constants.FOO;

//and for const:

Foo var1 = 479

5

Valore e metodo statici e costanti di Kotlin dichiarati

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
}

}

Accedi al valore ovunque

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

1
come definire un metodo globale o statico?
Samad Talukder,

@SamadTalukder In Kotlin sarà divertente sumValue (v1: Int, v2: Int): Int {return v1 + v2}
Shomu

5

Ad esempio val, le variabili definite con la constparola chiave sono immutabili. La differenza qui è che constviene utilizzato per le variabili conosciute in fase di compilazione.

Dichiarare una variabile constè come usare la staticparola chiave in Java.

Vediamo come dichiarare una variabile const in Kotlin:

const val COMMUNITY_NAME = "wiki"

E il codice analogo scritto in Java sarebbe:

final static String COMMUNITY_NAME = "wiki";

Aggiungendo alle risposte sopra -

@JvmField può essere utilizzato per indicare al compilatore di Kotlin di non generare getter / setter per questa proprietà ed esporlo come campo.

 @JvmField
 val COMMUNITY_NAME: "Wiki"

Campi statici

Le proprietà di Kotlin dichiarate in un oggetto denominato o in un oggetto associato avranno campi di supporto statici in tale oggetto denominato o nella classe contenente l'oggetto compagno.

Di solito questi campi sono privati ​​ma possono essere esposti in uno dei seguenti modi:

  • @JvmField annotazione;
  • lateinit modificatore;
  • const modificatore.

Maggiori dettagli qui - https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields


4

Qualcosa che non è menzionato in nessuna delle risposte è il sovraccarico dell'uso companion objects. Come puoi leggere qui , gli oggetti companion sono in realtà oggetti e la loro creazione consuma risorse. Inoltre, potrebbe essere necessario passare attraverso più di una funzione getter ogni volta che si utilizza la costante. Se tutto ciò di cui hai bisogno sono alcune costanti primitive, probabilmente starai meglio usando valper ottenere prestazioni migliori ed evitare companion object.

TL; DR; dell'articolo:

L'uso dell'oggetto associato in realtà trasforma questo codice

class MyClass {

    companion object {
        private val TAG = "TAG"
    }

    fun helloWorld() {
        println(TAG)
    }
}

In questo codice:

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

Quindi cerca di evitarli.


3

costanti locali:

const val NAME = "name"

Costanti globali:

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

accedi a MyConstants.NAME


1

Esistono alcuni modi per definire le costanti in Kotlin,

Utilizzo dell'oggetto associato

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

puoi usare sopra il blocco oggetto compagno all'interno di qualsiasi classe e definire tutti i tuoi campi all'interno di questo blocco stesso. Ma c'è un problema con questo approccio, dice la documentazione,

anche se i membri degli oggetti companion sembrano membri statici in altre lingue, in fase di esecuzione sono comunque membri di istanza di oggetti reali e possono, ad esempio, implementare interfacce.

Quando crei le tue costanti usando l'oggetto associato e vedi il bytecode decompilato , avrai qualcosa di simile al di sotto,

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

Da qui puoi facilmente vedere cosa diceva la documentazione, anche se i membri degli oggetti associati sembrano membri statici in altre lingue, in fase di esecuzione sono ancora membri di istanza di oggetti reali. Sta facendo un lavoro extra del necessario.

Ora arriva un altro modo, in cui non abbiamo bisogno di usare l'oggetto compagno come sotto,

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

Ancora una volta se vedi la versione decompilata del codice byte dello snippet sopra, troverai qualcosa del genere,

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

Ora se vedi il codice decompilato sopra, sta creando il metodo get per ogni variabile. Questo metodo get non è affatto richiesto.

Per sbarazzarsi di questi metodi get , dovresti usare const prima di val come sotto,

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

Ora se vedi il codice decompilato dello snippet sopra, troverai più facile da leggere poiché fa la conversione in background minima per il tuo codice.

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

    private ApiConstants() {
     }

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

Quindi questo è il modo migliore per creare costanti.


0

Per primitivi e archi:

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

Per altri casi:

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

Esempio:

/*
 * 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!"
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.