Konstanten in Kotlin - Was ist eine empfohlene Methode, um sie zu erstellen?


165

Wie wird empfohlen, Konstanten in Kotlin zu erstellen? Und wie lautet die Namenskonvention? Ich habe das nicht in der Dokumentation gefunden.

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

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

Oder ...?


4
Wenn Sie etwas möchten, das einem public static finalFeld in Java entspricht, verwenden Sie es const valin Ihrem Begleitobjekt. Wenn Sie ein private static finalFeld und einen öffentlichen Getter wünschen , verwenden Sie es valin Ihrem Begleitobjekt.
Michael

2
Hier ist der Blogpost, der erklärt, wie man Konstanten in Kotlin definiert: blog.egorand.me/where-do-i-put-my-constants-in-kotlin
Micer

Kasse diesen Artikel . Es gibt einen schönen Überblick über verschiedene Möglichkeiten, wie Sie Ihre Konstanten speichern können, mit entsprechenden Leistungskompromissen.
Feuerwehrmann

Antworten:


130

Wenn Sie in Kotlin die lokalen Konstanten erstellen möchten, mit denen in der Klasse verwendet werden soll, können Sie sie wie folgt erstellen

val MY_CONSTANT = "Constants"

Und wenn Sie eine öffentliche Konstante in Kotlin wie das öffentliche statische Finale in Java erstellen möchten , können Sie sie wie folgt erstellen.

companion object{

     const val MY_CONSTANT = "Constants"

}

3
Wie würde ich es in einer separaten Datei wie einer neuen Datei mit dem Namen verwenden Constants.ktoder wie?
Naveed Abbas

2
Ich benutze eine Datei für Konstanten. Behalte alle meine Konstanten drin.
filthy_wizard

2
Sie brauchen nicht die companion objectIch denke, @piotrpo Antwort sollte die akzeptierte sein
Chiara

@Chiara Das Begleitobjekt (und seine umschließende Klasse) dient im Gegensatz zu Deklarationen der obersten Ebene als Namespace. Ich denke, beide Antworten können je nach Situation sinnvoll sein.
Jingx

@jingx Ja, Sie haben dort einen Punkt, an dem Sie einen Namespace hinzufügen können, den Sie benötigen. : +1:
Chiara

118

Vermeiden Sie die Verwendung von Begleitobjekten. Hinter der Haube werden Getter- und Setter-Instanzmethoden erstellt, damit auf die Felder zugegriffen werden kann. Das Aufrufen von Instanzmethoden ist technisch teurer als das Aufrufen statischer Methoden.

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

Definieren Sie stattdessen die Konstanten in object.

Empfohlene Praxis :

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

und greifen Sie global wie folgt darauf zu: DbConstants.TABLE_USER_ATTRIBUTE_EMPID


Ist ein Begleitobjekt nicht ein Sonderfall eines Objekts? Wie kann sich ein const valObjekt in einem Begleitobjekt von einem Objekt const valin einem normalen Objekt unterscheiden (dh der einzige Unterschied zwischen Ihren Beispielen scheint darin zu bestehen, dass Sie constim Fall eines Begleitobjekts weggelassen haben - wenn Sie hinzufügen const, sollten die Beispiele dieselbe Leistung haben)
Erwin Bolwidt

1
@ErwinBolwidt Ich denke, @ sudeshs Punkt ist, dass man das Class-Wrapping-Companion-Object-Design nicht verwenden sollte, wenn der einzige Zweck der Struktur darin besteht, einen Namespace für einige konstante Werte bereitzustellen. Wenn Ihre Struktur jedoch instanziierbar sein und auch ein paar const vals enthalten muss, ist die Deklaration von a companion objectkorrekt.
Ari Lacenski

7
@ErwinBolwidt: sudesh ist richtig, der generierte Bytecode für Begleitobjekte beinhaltet eine zusätzliche Objekterstellung mit Gettern unter der Haube. Für eine gute Erklärung mit dekompilierten Kotlin-Beispielen siehe blog.egorand.me/where-do-i-put-my-constants-in-kotlin
dominik

2
Dank @dominik, ist dies ein sehr ausführlicher Artikel, empfehle ich diese an alle , die diese in der Tiefe verstehen wollen, gibt es viele Fälle , in denen Kotlin suboptimal Bytecode erzeugt, jetbrains hat viele solche Leistung bezogene Fehler behoben ... im Auge behalten diskutieren .kotlinlang.org werden Sie über viele dieser zugrunde liegenden Aspekte informiert.
Sudesh

1
Ich habe heute viel aus Ihrer Antwort gelernt @sudesh danke!
Rakhi Dhavale

34

Erstens ist die Namenskonvention in Kotlin für Konstanten dieselbe wie in Java (z. B. MY_CONST_IN_UPPERCASE).

Wie soll ich es erstellen?

1. Als Top-Level-Wert (empfohlen)

Sie müssen nur Ihre Konstante außerhalb Ihrer Klassendeklaration platzieren.

Zwei Möglichkeiten : Deklarieren Sie Ihre Konstante in Ihrer Klassendatei (Ihre Konstante hat eine klare Beziehung zu Ihrer Klasse).

private const val CONST_USED_BY_MY_CLASS = 1

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

Erstellen Sie eine dedizierte constants.kt-Datei, in der diese globalen const gespeichert werden (hier möchten Sie Ihre const in Ihrem Projekt weit verbreitet verwenden):

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

Dann müssen Sie es nur noch dort importieren, wo Sie es benötigen:

import com.project.constants

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

2. Deklarieren Sie es in einem Begleitobjekt (oder einer Objektdeklaration).

Dies ist viel weniger sauber, da unter der Haube beim Generieren von Bytecode ein nutzloses Objekt erstellt wird:

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

Noch schlimmer, wenn Sie es als Wert anstelle einer Konstante deklarieren (der Compiler generiert ein nutzloses Objekt + eine nutzlose Funktion):

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

Hinweis :

In Kotlin kann const nur primitive Typen enthalten. Wenn Sie eine Funktion an sie übergeben möchten, müssen Sie die Annotation @JvmField hinzufügen. Zur Kompilierungszeit wird es als öffentliche statische Endvariable transformiert. Aber es ist langsamer als bei einem primitiven Typ. Versuche es zu vermeiden.

@JvmField val foo = Foo()

Dies sollte die akzeptierte Antwort sein. sowieso in einem Fall wie: public static final Pattern REGEX_NOTEMPTY = Pattern.compile (". +") ????
Xan

23

Zum Zeitpunkt der Kompilierung bekannte Werte können (und sollten meiner Meinung nach) als konstant markiert werden.

Namenskonventionen sollten Java-Konventionen folgen und bei Verwendung aus Java-Code richtig sichtbar sein (mit Begleitobjekten ist dies jedoch irgendwie schwierig zu erreichen, aber trotzdem).

Die richtigen konstanten Deklarationen sind:

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

3
Naming conventions should follow Java ones- Warum?
Jodimoro

3
Kotlin befolgt normalerweise standardmäßig Java-Konventionen, sofern nicht anders angegeben, um die Interop reibungslos zu gestalten.
zsmb13

4
Wird so in der Dokumentation @Jodimoro kotlinlang.org/docs/reference/coding-conventions.html
Neil

2
@Neil ist es nicht.
Jodimoro

13
In diesem Link, den ich gepostet habe, heißt esIf in doubt, default to the Java Coding Conventions
Neil,

16

Sie benötigen keine Klasse, kein Objekt oder ein Begleitobjekt, um Konstanten in Kotlin zu deklarieren. Sie können einfach eine Datei deklarieren, die alle Konstanten enthält (z. B. Constants.kt, oder Sie können sie auch in eine vorhandene Kotlin-Datei einfügen) und die Konstanten direkt in der Datei deklarieren. Die zur Kompilierungszeit bekannten Konstanten müssen mit markiert sein const.

In diesem Fall sollte es also sein:

const val MY_CONST = "something"

und dann können Sie die Konstante importieren mit:

import package_name.MY_CONST

Sie können auf diesen Link verweisen


13
Konstanten müssen in der Klasse sein, auf die sie sich beziehen. Wenn Sie eine 'Konstanten'-Klasse erstellen, werden Sie schließlich Hunderte von Konstanten darin beenden. Pe: MAX_WIDTH, MAX_HEIGHT muss in der Screen-Klasse sein, damit Sie logisch darauf zugreifen können: Screen.MAX_WIDTH, und Sie müssen keine Constants.SCREEN_MAX_WIDTH einfügen, die in 2 Jahren mit Constants.SCR_MAX_W und Constants.MAX_WIDTH dupliziert werden, da NOBODY Scrollt Hunderte / Tausend Zeilen nach unten, wenn Sie Strg + Leertaste drücken, um die automatische Vervollständigung zu aktivieren. Im Ernst: Tu es nicht. führt zu Unwartbarkeit
inigoD

1
@inigoD Das stimmt, wenn Sie die Konstante an einem Ort oder nur bei Kindern verwenden, aber dies ist kaum jemals der Fall. Wenn Sie die Konstante in eine dunkle Klasse einfügen, vergessen Sie sie oder übernehmen mit größerer Wahrscheinlichkeit eine Codebasis. Sie können sie duplizieren. Oder es ist nicht klar, wo sie platziert werden sollen. Die Quelle oder das Ziel? Sie können mehrere konstante Dateien erstellen, die leicht zu finden sind. Einer für Präferenzschlüssel, einer für Anforderungsschlüssel, einer für Ansichtskonstanten usw.
Herrbert74

1
@ Herrbert74 Es tut mir leid, aber ich muss dir nicht zustimmen. Ich stimme zu, dass es manchmal schwierig sein kann, das zu finden, aber ein konstanter Ort sollte immer die Klasse sein, die mehr damit zu tun hat. Und das zufällige Speichern in einer Zufallszahlendatei ist nicht der beste Weg, wenn Sie sie später abrufen möchten ... Sie werden argumentieren, dass sie nicht zufällig gespeichert werden, sondern in Paketen, auf die sich die Konstanten beziehen, aber das ist nur eine Entschuldigung dafür
Setzen

4
Wenn eine Konstante wirklich global ist oder einen großen Bereich hat, z. B. einen Wert für eine Annotation, die für alle Pakete verwendet wird, oder einen Headernamen, der von mehreren Controllern usw. abgerufen wird, ist es völlig akzeptabel, Konstanten zu erstellen Klasse ", die angemessen ist. Konstanten, die nur in bestimmten Kontexten verwendet werden, sollten jedoch auf diesen Kontext beschränkt und in der entsprechenden Klasse deklariert werden.
Nephthys76

@ Nephthys76 Nur als Hinweis, für " wie einen Wert für eine Annotation, die für alle Pakete verwendet wird ", würde ich sagen, dass der beste Platz für die Konstante in der Annotationsklasse liegt.
Slaw

8

Wenn Sie Ihren const val valName = valValuevor den Klassennamen setzen, wird auf diese Weise ein erstellt

public static final YourClass.Ktdas wird die public static finalWerte haben.

Kotlin :

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

Java dekompiliert:

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

Ist das wahr? Hat jemand Erfahrung mit dieser Methode?
Scott Biggs

5
class Myclass {

 companion object {
        const val MYCONSTANT = 479
}

Sie haben zwei Möglichkeiten: Sie können das constSchlüsselwort oder das Schlüsselwort verwenden @JvmField, wodurch es zur statischen Endkonstante von Java wird.

class Myclass {

     companion object {
           @JvmField val MYCONSTANT = 479
    }

Wenn Sie die @JvmFieldAnnotation verwenden, wird die Konstante nach dem Kompilieren für Sie so eingegeben, wie Sie es in Java nennen würden.
Genau wie Sie es in Java aufrufen würden, ersetzt der Compiler dies für Sie, wenn Sie die Companion-Konstante im Code aufrufen.

Wenn Sie jedoch das Schlüsselwort const verwenden, wird der Wert der Konstante inline. Mit Inline meine ich, dass der tatsächliche Wert nach dem Kompilieren verwendet wird.

Zusammenfassend lässt sich sagen, was der Compiler für Sie tun wird:

//so for @JvmField:

Foo var1 = Constants.FOO;

//and for const:

Foo var1 = 479

5

Kotlin statische und konstante Wert & Methode deklarieren

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
}

}}

Zugriffswert überall

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

1
Wie definiere ich eine globale oder statische Methode?
Samad Talukder

@SamadTalukder In Kotlin wird es Spaß machen sumValue (v1: Int, v2: Int): Int {return v1 + v2}
Shomu

5

Ebenso valsind mit dem constSchlüsselwort definierte Variablen unveränderlich. Der Unterschied besteht darin, dass conster für Variablen verwendet wird, die zur Kompilierungszeit bekannt sind.

Das Deklarieren einer Variablen constähnelt dem Verwenden des staticSchlüsselworts in Java.

Mal sehen, wie man eine const-Variable in Kotlin deklariert:

const val COMMUNITY_NAME = "wiki"

Und der in Java geschriebene analoge Code wäre:

final static String COMMUNITY_NAME = "wiki";

Hinzufügen zu den obigen Antworten -

@JvmField Dies kann verwendet werden, um den Kotlin-Compiler anzuweisen, keine Getter / Setter für diese Eigenschaft zu generieren und sie als Feld verfügbar zu machen.

 @JvmField
 val COMMUNITY_NAME: "Wiki"

Statische Felder

Kotlin-Eigenschaften, die in einem benannten Objekt oder einem Begleitobjekt deklariert sind, haben statische Hintergrundfelder entweder in diesem benannten Objekt oder in der Klasse, die das Begleitobjekt enthält.

Normalerweise sind diese Felder privat, aber sie können auf eine der folgenden Arten verfügbar gemacht werden:

  • @JvmField Anmerkung;
  • lateinit Modifikator;
  • const Modifikator.

Weitere Details finden Sie hier - https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields


4

Etwas, das in keiner der Antworten erwähnt wird, ist der Aufwand für die Verwendung companion objects. Wie Sie hier lesen können , sind Begleitobjekte tatsächlich Objekte, und das Erstellen dieser Objekte verbraucht Ressourcen. Darüber hinaus müssen Sie möglicherweise jedes Mal, wenn Sie Ihre Konstante verwenden, mehr als eine Getter-Funktion durchlaufen. Wenn Sie nur ein paar primitive Konstanten benötigen, sind Sie wahrscheinlich besser dran val, um eine bessere Leistung zu erzielen und die zu vermeiden companion object.

TL; DR; des Artikels:

Die Verwendung eines Begleitobjekts dreht diesen Code tatsächlich

class MyClass {

    companion object {
        private val TAG = "TAG"
    }

    fun helloWorld() {
        println(TAG)
    }
}

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

Also versuche sie zu vermeiden.


3

lokale Konstanten:

const val NAME = "name"

Globale Konstanten:

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

Zugriff auf MyConstants.NAME


1

Es gibt einige Möglichkeiten, wie Sie Konstanten in Kotlin definieren können:

Begleitobjekt verwenden

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

Sie können den obigen Begleitobjektblock in jeder Klasse verwenden und alle Ihre Felder in diesem Block selbst definieren. Bei diesem Ansatz gibt es jedoch ein Problem, heißt es in der Dokumentation:

Obwohl die Mitglieder von Begleitobjekten in anderen Sprachen wie statische Elemente aussehen, sind diese zur Laufzeit immer noch Instanzmitglieder von realen Objekten und können beispielsweise Schnittstellen implementieren.

Wenn Sie Ihre Konstanten mit einem Begleitobjekt erstellen und den dekompilierten Bytecode sehen , sehen Sie wie folgt aus:

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

Von hier aus können Sie leicht sehen, was in der Dokumentation gesagt wurde, obwohl die Mitglieder von Begleitobjekten in anderen Sprachen wie statische Elemente aussehen. Zur Laufzeit sind dies immer noch Instanzmitglieder von realen Objekten. Es erledigt zusätzliche Arbeit als erforderlich.

Jetzt kommt ein anderer Weg, bei dem wir kein Begleitobjekt wie unten verwenden müssen.

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

Wenn Sie wieder die dekompilierte Version des Bytecodes des obigen Snippets sehen, werden Sie so etwas finden:

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

Wenn Sie nun den oben dekompilierten Code sehen, wird für jede Variable eine get-Methode erstellt. Diese get-Methode ist überhaupt nicht erforderlich.

Um diese get-Methoden loszuwerden , sollten Sie const vor val wie unten verwenden.

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

Wenn Sie nun den dekompilierten Code des obigen Snippets sehen, ist das Lesen einfacher, da der Code am wenigsten im Hintergrund konvertiert wird.

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

    private ApiConstants() {
     }

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

Dies ist also der beste Weg, um Konstanten zu erstellen.


0

Für Primitive und Strings:

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

Für andere Fälle:

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

Beispiel:

/*
 * 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!"
        }
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.