Constantes dans Kotlin - quelle est la méthode recommandée pour les créer?

165

Comment est-il recommandé de créer des constantes dans Kotlin? Et quelle est la convention de dénomination? Je n'ai pas trouvé cela dans la documentation.

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

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

Ou ...?

Jodimoro
la source
4
Si vous voulez quelque chose correspondant à un public static finalchamp en Java, utilisez const valdans votre objet compagnon. Si vous voulez un private static finalchamp et un getter public, utilisez valdans votre objet compagnon.
Michael
2
Voici l'article de blog qui explique comment définir les constantes dans Kotlin: blog.egorand.me/where-do-i-put-my-constants-in-kotlin
Micer
Consultez cet article . Il donne un bon aperçu des différentes façons dont vous pouvez stocker vos constantes, avec des compromis de performance associés.
firedrillsergeant

Réponses:

132

Dans Kotlin, si vous souhaitez créer les constantes locales qui sont censées être utilisées dans la classe, vous pouvez la créer comme ci-dessous

val MY_CONSTANT = "Constants"

Et si vous souhaitez créer une constante publique dans kotlin comme public static final en java, vous pouvez la créer comme suit.

companion object{

     const val MY_CONSTANT = "Constants"

}
AaRiF
la source
3
Comment puis-je l'utiliser dans un fichier séparé comme un nouveau fichier nommé Constants.ktou comment?
Naveed Abbas du
2
j'utilise un fichier pour les constantes. garde toutes mes constantes là-dedans.
filthy_wizard
2
vous n'avez pas besoin de la companion objectréponse Je pense que @piotrpo devrait être celle acceptée
Chiara
@Chiara l'objet compagnon (et sa classe englobante) sert d'espace de noms, par opposition aux déclarations de niveau supérieur. Je pense que les deux réponses peuvent avoir un sens selon la situation.
jingx
@jingx oui, vous avez là un point pour y ajouter un espace de noms dont vous en avez besoin. : +1:
Chiara
118

Évitez d'utiliser des objets compagnons. Derrière le capot, des méthodes d'instance getter et setter sont créées pour que les champs soient accessibles. L'appel de méthodes d'instance est techniquement plus coûteux que l'appel de méthodes statiques.

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

Définissez plutôt les constantes dans object.

Pratique recommandée :

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

et accédez-y globalement comme ceci: DbConstants.TABLE_USER_ATTRIBUTE_EMPID

sudesh
la source
Un objet compagnon n'est-il pas un cas particulier d'un objet? Comment un const valdans un objet compagnon peut-il être différent de a const valdans un objet ordinaire (c'est-à-dire que la seule différence entre vos exemples semble être que vous avez omis constdans le cas de l'objet compagnon - si vous ajoutez const, les exemples devraient avoir les mêmes performances)
Erwin Bolwidt
1
@ErwinBolwidt Je pense que le point de @ sudesh est qu'il ne faut pas utiliser la conception d'objet compagnon-enveloppant de classe lorsque le seul but de la structure est de fournir un espace de noms pour certaines valeurs constantes. Mais si votre structure doit être instanciable et inclure également quelques const vals, déclarer a companion objectest correct.
Ari Lacenski
7
@ErwinBolwidt: sudesh a raison, le bytecode généré pour les objets compagnons implique la création d'objets supplémentaires avec des getters sous le capot. Pour une bonne explication avec des exemples de kotlin décompilés, voir blog.egorand.me/where-do-i-put-my-constants-in-kotlin
dominik
2
merci @dominik, c'est un article très détaillé, je le recommande à tous ceux qui veulent comprendre cela en profondeur, il existe de nombreux cas où kotlin produit un bytecode sous-optimal, les jetbrains ont résolu de nombreux bugs liés aux performances ... gardez un œil sur la discussion .kotlinlang.org , vous serez informé de nombreux aspects sous-jacents.
sudesh
1
J'ai beaucoup appris de votre réponse aujourd'hui @sudesh merci!
Rakhi Dhavale
34

Tout d'abord , la convention de dénomination dans Kotlin pour les constantes est la même que dans java (par exemple: MY_CONST_IN_UPPERCASE).

Comment dois-je le créer?

1. En tant que valeur de niveau supérieur (recommandé)

Il vous suffit de mettre votre const en dehors de votre déclaration de classe.

Deux possibilités : Déclarez votre const dans votre fichier de classe (votre const a une relation claire avec votre classe)

private const val CONST_USED_BY_MY_CLASS = 1

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

Créez un fichier dédié constants.kt où stocker ces const globales (ici, vous souhaitez utiliser votre const largement dans votre projet):

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

Ensuite, il vous suffit de l'importer là où vous en avez besoin:

import com.project.constants

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

2. Déclarez-le dans un objet compagnon (ou une déclaration d'objet)

C'est beaucoup moins propre car sous le capot, lorsque le bytecode est généré, un objet inutile est créé:

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

Pire encore si vous le déclarez comme un val au lieu d'un const (le compilateur générera un objet inutile + une fonction inutile):

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

Remarque :

Dans kotlin, const ne peut contenir que des types primitifs. Si vous souhaitez lui transmettre une fonction, vous devez ajouter l'annotation @JvmField. Au moment de la compilation, il sera transformé en une variable finale statique publique. Mais c'est plus lent qu'avec un type primitif. Essayez de l'éviter.

@JvmField val foo = Foo()
A.Mamode
la source
cela devrait être la réponse acceptée. de toute façon dans un cas comme: motif final statique public REGEX_NOTEMPTY = Pattern.compile (". +") ????
Xan
24

Les valeurs connues au moment de la compilation peuvent (et à mon avis devraient) être marquées comme constantes.

Les conventions de dénomination doivent suivre celles de Java et doivent être correctement visibles lorsqu'elles sont utilisées à partir du code Java (c'est en quelque sorte difficile à réaliser avec des objets compagnons, mais de toute façon).

Les déclarations de constantes appropriées sont:

const val MY_CONST = "something"
const val MY_INT = 1
piotrpo
la source
3
Naming conventions should follow Java ones- Pourquoi?
Jodimoro
3
Kotlin suit généralement les conventions Java par défaut, sauf indication contraire, pour rendre l'interopérabilité fluide.
zsmb13
4
Est spécifié comme ça dans la documentation @Jodimoro kotlinlang.org/docs/reference/coding-conventions.html
Neil
2
@Neil, ce n'est pas le cas.
Jodimoro
13
Dans ce lien, j'ai posté ils disentIf in doubt, default to the Java Coding Conventions
Neil
16

Vous n'avez pas besoin d'une classe, d'un objet ou d'un objet compagnon pour déclarer des constantes dans Kotlin. Vous pouvez simplement déclarer un fichier contenant toutes les constantes (par exemple Constants.kt ou vous pouvez également les placer dans n'importe quel fichier Kotlin existant) et déclarer directement les constantes à l'intérieur du fichier. Les constantes connues au moment de la compilation doivent être marquées de const.

Donc, dans ce cas, cela devrait être:

const val MY_CONST = "something"

puis vous pouvez importer la constante en utilisant:

import package_name.MY_CONST

Vous pouvez vous référer à ce lien

Abdul Wadood
la source
13
Les constantes doivent être dans la classe à laquelle elles sont liées. Si vous créez une classe «Constantes», vous finirez par finir par contenir des centaines de constantes. Pe: MAX_WIDTH, MAX_HEIGHT doit être dans la classe Screen pour que vous puissiez y accéder logiquement: Screen.MAX_WIDTH et vous n'avez pas besoin de mettre Constants.SCREEN_MAX_WIDTH qui sera dupliqué avec Constants.SCR_MAX_W et Constants.MAX_WIDTH dans 2 ans car NOBODY fait défiler des centaines / milliers de lignes vers le bas lorsqu'ils appuient sur Ctrl + espace pour la saisie semi-automatique. Sérieusement: ne le faites pas. conduit à l'inconsistance
inigoD
1
@inigoD C'est vrai si vous utilisez la constante à un seul endroit ou seulement chez les enfants, mais ce n'est presque jamais le cas. Si vous placez la constante dans une classe obscure, vous l'oubliez ou plus probablement vous reprenez une base de code, vous risquez de la dupliquer. Ou il n'est pas évident de savoir où les mettre. La source ou la destination? Vous pouvez créer plusieurs fichiers constants, faciles à trouver. Un pour les clés de préférence, un pour les clés de requête, un pour les constantes d'affichage, etc.
Herrbert74
1
@ Herrbert74 Je suis désolé mais je ne suis pas d'accord avec vous. Je suis d'accord sur le fait que parfois il peut être difficile de trouver ce qui est, mais un endroit constant devrait toujours être la classe qui lui est le plus liée. Et les enregistrer au hasard dans des fichiers de nombres aléatoires n'est pas la meilleure façon si vous souhaitez les récupérer plus tard ... Vous soutiendrez qu'ils ne seraient pas stockés au hasard mais dans des packages auxquels les constantes sont liées, mais ce n'est qu'une excuse pour ne pas les mettre dans les classes auxquelles ils sont liés qui est, à la fin, leur place ...
inigoD
4
Si une constante est vraiment globale ou a une grande portée ... telle qu'une valeur pour une annotation utilisée dans tous les packages, ou un nom d'en-tête qui est récupéré par plusieurs contrôleurs, etc., alors il est tout à fait acceptable de créer des "constantes class "dont la portée est appropriée. Cependant, les constantes qui ne sont utilisées que dans des contextes spécifiques doivent être étendues à ce contexte et déclarées dans la classe appropriée.
Nephthys76
@ Nephthys76 Juste comme note, pour " comme une valeur pour une annotation utilisée dans tous les packages " spécifiquement, je dirais que le meilleur endroit pour la constante est dans la classe d'annotation.
Slaw
8

Si vous mettez votre const val valName = valValueavant le nom de la classe, cela créera un

public static final YourClass.Ktqui aura les public static finalvaleurs.

Kotlin :

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

Java décompilé:

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
la source
Est-ce vrai? Quelqu'un a-t-il une expérience avec cette méthode?
Scott Biggs
5
class Myclass {

 companion object {
        const val MYCONSTANT = 479
}

vous avez deux choix, vous pouvez utiliser le constmot-clé ou utiliser le @JvmFieldqui en fait une constante finale statique de Java.

class Myclass {

     companion object {
           @JvmField val MYCONSTANT = 479
    }

Si vous utilisez l' @JvmFieldannotation, une fois compilée, la constante est insérée pour vous comme vous l'appeleriez en java.
Tout comme vous l'appeleriez en java, le compilateur remplacera cela pour vous lorsque vous appelez la constante compagnon dans le code.

Cependant, si vous utilisez le mot-clé const, la valeur de la constante est insérée. Par inline, je veux dire que la valeur réelle est utilisée après sa compilation.

Donc, pour résumer, voici ce que le compilateur fera pour vous:

//so for @JvmField:

Foo var1 = Constants.FOO;

//and for const:

Foo var1 = 479
j2emanue
la source
5

Déclaration de la valeur et de la méthode statiques et constantes de Kotlin

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
}

}

Accédez à la valeur n'importe où

val value = MyConstant.PI
val value = MyConstant.sumValue(10,5)
Shomu
la source
1
comment définir une méthode globale ou statique?
Samad Talukder le
@SamadTalukder À Kotlin, ce sera amusant sumValue (v1: Int, v2: Int): Int {return v1 + v2}
Shomu le
5

Comme val, les variables définies avec le constmot-clé sont immuables. La différence ici est qu'elle constest utilisée pour les variables connues au moment de la compilation.

Déclarer une variable constest un peu comme utiliser le staticmot - clé en Java.

Voyons comment déclarer une variable const dans Kotlin:

const val COMMUNITY_NAME = "wiki"

Et le code analogue écrit en Java serait:

final static String COMMUNITY_NAME = "wiki";

Ajout aux réponses ci-dessus -

@JvmField an être utilisé pour indiquer au compilateur Kotlin de ne pas générer de getters / setters pour cette propriété et de l'exposer en tant que champ.

 @JvmField
 val COMMUNITY_NAME: "Wiki"

Champs statiques

Les propriétés Kotlin déclarées dans un objet nommé ou un objet compagnon auront des champs de sauvegarde statiques soit dans cet objet nommé, soit dans la classe contenant l'objet compagnon.

Habituellement, ces champs sont privés, mais ils peuvent être exposés de l'une des manières suivantes:

  • @JvmField annotation;
  • lateinit modificateur;
  • const modificateur.

Plus de détails ici - https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields

Anoop M
la source
4

Quelque chose qui n'est mentionné dans aucune des réponses est la surcharge d'utilisation companion objects. Comme vous pouvez le lire ici , les objets compagnons sont en fait des objets et leur création consomme des ressources. De plus, vous devrez peut-être utiliser plusieurs fonctions getter chaque fois que vous utilisez votre constante. Si tout ce dont vous avez besoin est de quelques constantes primitives, vous serez probablement mieux d'utiliser valpour obtenir de meilleures performances et éviter le companion object.

TL, DR; de l'article:

L'utilisation d'un objet compagnon transforme ce code

class MyClass {

    companion object {
        private val TAG = "TAG"
    }

    fun helloWorld() {
        println(TAG)
    }
}

Dans ce code:

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

Alors essayez de les éviter.

Sir Codesalot
la source
3

constantes locales:

const val NAME = "name"

Constantes globales:

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

accéder à MyConstants.NAME

Amjed Baig
la source
1

Il existe plusieurs façons de définir des constantes dans Kotlin,

Utilisation d'un objet compagnon

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

vous pouvez utiliser le bloc d'objet compagnon ci-dessus dans n'importe quelle classe et définir tous vos champs à l'intérieur de ce bloc lui-même. Mais il y a un problème avec cette approche, dit la documentation,

même si les membres des objets compagnons ressemblent à des membres statiques dans d'autres langages, au moment de l'exécution, ils sont toujours des membres d'instance d'objets réels et peuvent, par exemple, implémenter des interfaces.

Lorsque vous créez vos constantes à l'aide d'un objet compagnon et que vous voyez le bytecode décompilé , vous aurez quelque chose comme ci-dessous,

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

De là, vous pouvez facilement voir ce que dit la documentation, même si les membres des objets compagnons ressemblent à des membres statiques dans d'autres langages, au moment de l'exécution, ils sont toujours des membres d'instance d'objets réels. Cela fait un travail supplémentaire que nécessaire.

Vient maintenant une autre façon, où nous n'avons pas besoin d'utiliser un objet compagnon comme ci-dessous,

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

Encore une fois, si vous voyez la version décompilée du code d'octet de l'extrait de code ci-dessus, vous trouverez quelque chose comme ça,

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

Maintenant, si vous voyez le code décompilé ci-dessus, il crée une méthode get pour chaque variable. Cette méthode get n'est pas du tout requise.

Pour vous débarrasser de ces méthodes get , vous devez utiliser const avant val comme ci-dessous,

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

Maintenant, si vous voyez le code décompilé de l'extrait de code ci-dessus, vous le trouverez plus facile à lire car il effectue la moindre conversion en arrière-plan pour votre code.

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

    private ApiConstants() {
     }

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

C'est donc la meilleure façon de créer des constantes.

Abhishek Kumar
la source
0

Pour les primitives et les chaînes:

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

Pour les autres cas:

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

Exemple:

/*
 * 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!"
        }
    }
}
Alexandre Savin
la source