Stałe w Kotlinie - jaki jest zalecany sposób ich tworzenia?

165

Jak jest zalecane tworzenie stałych w Kotlinie? Jaka jest konwencja nazewnictwa? Nie znalazłem tego w dokumentacji.

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

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

Albo ...?

Jodimoro
źródło
4
Jeśli chcesz, aby coś odpowiadało public static finalpolu w Javie, użyj const valw obiekcie towarzyszącym. Jeśli chcesz mieć private static finalpole i publiczny getter, użyj valw swoim obiekcie towarzyszącym.
Michael
2
Oto post na blogu, który wyjaśnia sposoby definiowania stałych w Kotlinie: blog.egorand.me/where-do-i-put-my-constants-in-kotlin
Micer
Sprawdź ten artykuł . Daje dobry przegląd różnych sposobów przechowywania stałych, z powiązanymi kompromisami wydajnościowymi.
firedrillsergeant

Odpowiedzi:

132

W Kotlinie, jeśli chcesz stworzyć lokalne stałe, które mają być używane w klasie, możesz to zrobić jak poniżej

val MY_CONSTANT = "Constants"

A jeśli chcesz utworzyć stałą publiczną w kotlinie, taką jak publiczny statyczny finał w java, możesz utworzyć ją w następujący sposób.

companion object{

     const val MY_CONSTANT = "Constants"

}
AaRiF
źródło
3
Jak użyć go w osobnym pliku, takim jak nowy plik o nazwie Constants.ktlub w jaki sposób?
Naveed Abbas,
2
używam pliku dla stałych. trzymaj tam wszystkie moje stałe.
filthy_wizard
2
nie potrzebujesz odpowiedzi companion objectMyślę, że @piotrpo powinna być zaakceptowana
Chiara
@Chiara, obiekt towarzyszący (i otaczająca go klasa), służy jako przestrzeń nazw, w przeciwieństwie do deklaracji najwyższego poziomu. Myślę, że obie odpowiedzi mogą mieć sens w zależności od sytuacji.
jingx
@jingx tak, masz sens, aby dodać do niego przestrzeń nazw, której potrzebujesz. : +1:
Chiara
118

Unikaj używania obiektów towarzyszących. Za maską tworzone są metody instancji getter i setter, aby pola były dostępne. Wywoływanie metod instancji jest technicznie droższe niż wywoływanie metod statycznych.

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

Zamiast tego zdefiniuj stałe w object.

Zalecana praktyka :

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

i uzyskaj do nich globalny dostęp w następujący sposób: DbConstants.TABLE_USER_ATTRIBUTE_EMPID

sudesh
źródło
Czy obiekt towarzyszący nie jest specjalnym przypadkiem obiektu? W jaki sposób const valobiekt towarzyszący może różnić się od a const valw zwykłym obiekcie (tj. Jedyną różnicą między Twoimi przykładami wydaje się być to, że pominięto go constw przypadku obiektu towarzyszącego - jeśli dodasz const, przykłady powinny mieć taką samą wydajność)
Erwin Bolwidt
1
@ErwinBolwidt Myślę, że celem @ sudesh jest to, że nie należy używać projektowania obiektów towarzyszących zawijania klas, gdy jedynym celem struktury jest zapewnienie przestrzeni nazw dla niektórych wartości stałych. Ale jeśli twoja struktura musi być natychmiastowa, a także zawierać kilka const vals, zadeklarowanie a companion objectjest poprawne.
Ari Lacenski
7
@ErwinBolwidt: sudesh ma rację, wygenerowany kod bajtowy dla obiektów towarzyszących obejmuje dodatkowe tworzenie obiektów z getterami pod maską. Aby uzyskać dobre wyjaśnienie z zdekompilowanymi
dominik
2
dzięki @dominik, jest to bardzo szczegółowy artykuł, polecam to każdemu, kto chce to dogłębnie zrozumieć, jest wiele przypadków, w których kotlin produkuje nieoptymalny kod bajtowy, odrzutowce rozwiązały wiele takich błędów związanych z wydajnością ... obserwuj dyskusję .kotlinlang.org , zostaniesz poinformowany o wielu takich podstawowych aspektach.
sudesh
1
Wiele się nauczyłem z Twojej dzisiejszej odpowiedzi @sudesh thanks!
Rakhi Dhavale
34

Po pierwsze , konwencja nazewnictwa stałych w Kotlinie jest taka sama jak w javie (np .: MY_CONST_IN_UPPERCASE).

Jak mam to stworzyć?

1. Jako wartość najwyższego poziomu (zalecane)

Musisz tylko umieścić swoją const poza deklaracją klasy.

Dwie możliwości : zadeklaruj swoją stałą w pliku klasy (twoja stała ma wyraźny związek z twoją klasą)

private const val CONST_USED_BY_MY_CLASS = 1

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

Utwórz dedykowany plik constants.kt, w którym będziesz przechowywać te globalne stałe (tutaj chcesz używać swojej stałej w całym projekcie):

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

Następnie wystarczy zaimportować go tam, gdzie jest potrzebny:

import com.project.constants

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

2. Zadeklaruj go w obiekcie towarzyszącym (lub deklaracji obiektu)

Jest to znacznie mniej czystsze, ponieważ pod maską, podczas generowania kodu bajtowego, tworzony jest bezużyteczny obiekt:

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

Jeszcze gorzej, jeśli zadeklarujesz to jako wartość zamiast stałej (kompilator wygeneruje bezużyteczny obiekt + bezużyteczną funkcję):

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

Uwaga :

W kotlinie const może po prostu zawierać typy pierwotne. Jeśli chcesz przekazać do niego funkcję, musisz dodać adnotację @JvmField. W czasie kompilacji zostanie przekształcona jako publiczna statyczna zmienna końcowa. Ale jest wolniejszy niż w przypadku typu prymitywnego. Staraj się tego unikać.

@JvmField val foo = Foo()
A.Mamode
źródło
powinna to być akceptowana odpowiedź. w każdym razie w przypadku typu: public static final Pattern REGEX_NOTEMPTY = Pattern.compile (". +") ????
Xan
23

Wartości znane w czasie kompilacji mogą (i moim zdaniem powinny) być oznaczone jako stałe.

Konwencje nazewnictwa powinny być zgodne z konwencjami języka Java i powinny być odpowiednio widoczne, gdy są używane z kodu Java (jest to trudne do osiągnięcia w przypadku obiektów towarzyszących, ale i tak).

Właściwe deklaracje stałe to:

const val MY_CONST = "something"
const val MY_INT = 1
piotrpo
źródło
3
Naming conventions should follow Java ones- czemu?
Jodimoro
3
Kotlin zwykle domyślnie przestrzega konwencji Java, jeśli nie określono inaczej, aby zapewnić płynną współpracę.
zsmb13
4
Jest tak określone w dokumentacji @Jodimoro kotlinlang.org/docs/reference/coding-conventions.html
Neil
2
@Neil, to nie jest.
Jodimoro
13
W tym linku, który zamieściłem, mówiąIf in doubt, default to the Java Coding Conventions
Neil
16

Nie potrzebujesz klasy, obiektu ani obiektu towarzyszącego do deklarowania stałych w Kotlinie. Możesz po prostu zadeklarować plik zawierający wszystkie stałe (na przykład Constants.kt lub możesz również umieścić je w dowolnym istniejącym pliku Kotlin) i bezpośrednio zadeklarować stałe w pliku. Stałe znane w czasie kompilacji muszą być oznaczone const.

Tak więc w tym przypadku powinno to być:

const val MY_CONST = "something"

a następnie możesz zaimportować stałą za pomocą:

import package_name.MY_CONST

Możesz odnieść się do tego linku

Abdul Wadood
źródło
13
Stałe muszą znajdować się w klasie, z którą są związane. Jeśli utworzysz klasę „Constants”, w końcu skończysz z setkami stałych w niej zawartych. Pe: MAX_WIDTH, MAX_HEIGHT musi znajdować się w klasie Screen, aby można było uzyskać do niego logiczny dostęp: Screen.MAX_WIDTH i nie trzeba umieszczać stałych.SCREEN_MAX_WIDTH, które zostaną zduplikowane ze stałymi.SCR_MAX_W i stałymi.MAX_WIDTH za 2 lata, ponieważ NOBODY przewija setki / tysiące wierszy w dół po naciśnięciu Ctrl + spacja w celu autouzupełniania. Poważnie: nie rób tego. prowadzi do
niemożności utrzymania
1
@inigoD To prawda, jeśli używasz stałej w jednym miejscu lub tylko u dzieci, ale rzadko tak jest. Jeśli umieścisz stałą w niejasnej klasie, zapomnisz o niej lub, co bardziej prawdopodobne, przejmiesz bazę kodu, możesz je powielić. Albo nie jest oczywiste, gdzie je umieścić. Źródło czy cel? Możesz utworzyć kilka stałych plików, które są łatwe do znalezienia. Jeden dla kluczy preferencji, jeden dla kluczy żądań, jeden dla stałych widoku itd.
Herrbert74
1
@ Herrbert74 Przepraszam, ale muszę się z tobą nie zgodzić. Zgadzam się, że czasami trudno jest znaleźć, która jest, ale zawsze powinna być stała klasa, która jest z nią bardziej związana. A zapisywanie ich losowo w plikach o losowej liczbie nie jest najlepszym sposobem, jeśli chcesz je później odzyskać ... Będziesz argumentował, że nie będą one przechowywane losowo, ale w pakietach, z którymi powiązane są stałe, ale to tylko wymówka nie umieszczać ich na zajęciach, z którymi są związane, czyli na końcu ich miejsce ...
inigoD
4
Jeśli stała jest prawdziwie globalna lub ma duży zakres ... na przykład wartość dla adnotacji używanej we wszystkich pakietach lub nazwa nagłówka, która jest pobierana przez wiele kontrolerów itp., Wtedy jest całkowicie dopuszczalne utworzenie „stałych class ”, która ma odpowiedni zakres. Jednak stałe, które są używane tylko w określonych kontekstach, powinny być objęte zakresem tego kontekstu i zadeklarowane w odpowiedniej klasie.
Nephthys76
@ Nephthys76 Jako uwaga, konkretnie w przypadku „ np. Wartości adnotacji używanej we wszystkich pakietach ”, powiedziałbym, że najlepszym miejscem na stałą jest klasa adnotacji.
Slaw
8

Jeśli umieścisz swój const val valName = valValueprzed nazwą klasy, w ten sposób utworzy się plik

public static final YourClass.Ktktóre będą miały public static finalwartości.

Kotlin :

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

Dekompilacja Java:

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
źródło
Czy to prawda? Czy ktoś ma jakieś doświadczenie z tą metodą?
Scott Biggs
5
class Myclass {

 companion object {
        const val MYCONSTANT = 479
}

masz dwie możliwości: możesz użyć constsłowa kluczowego lub użyć, @JvmFieldco sprawia, że ​​jest to statyczna stała java.

class Myclass {

     companion object {
           @JvmField val MYCONSTANT = 479
    }

Jeśli użyjesz @JvmFieldadnotacji, to po jej kompilacji stała zostanie wstawiona tak, jak byś ją nazwał w java.
Tak jak byś to nazwał w java, kompilator zastąpi to za Ciebie, gdy wywołasz stałą towarzyszącą w kodzie.

Jeśli jednak użyjesz słowa kluczowego const, wartość stałej zostanie wstawiona. Przez inline mam na myśli faktyczną wartość używaną po kompilacji.

więc podsumowując, oto co kompilator zrobi za Ciebie:

//so for @JvmField:

Foo var1 = Constants.FOO;

//and for const:

Foo var1 = 479
j2emanue
źródło
5

Deklaracja wartości statycznej i stałej Kotlin i metody

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
}

}

Uzyskaj dostęp do wartości w dowolnym miejscu

val value = MyConstant.PI
val value = MyConstant.sumValue(10,5)
Shomu
źródło
1
jak zdefiniować metodę globalną czy statyczną?
Samad Talukder
@SamadTalukder W Kotlinie będzie fajnie sumValue (v1: Int, v2: Int): Int {return v1 + v2}
Shomu
5

Podobnie jak valzmienne zdefiniowane za pomocą constsłowa kluczowego są niezmienne. Różnica polega na tym, że constjest używany dla zmiennych znanych w czasie kompilacji.

Deklarowanie zmiennej constprzypomina użycie staticsłowa kluczowego w Javie.

Zobaczmy, jak zadeklarować zmienną const w Kotlinie:

const val COMMUNITY_NAME = "wiki"

Analogiczny kod napisany w Javie to:

final static String COMMUNITY_NAME = "wiki";

Dodanie do powyższych odpowiedzi -

@JvmField an służy do poinstruowania kompilatora Kotlin, aby nie generował metod pobierających / ustawiających dla tej właściwości i uwidaczniał ją jako pole.

 @JvmField
 val COMMUNITY_NAME: "Wiki"

Pola statyczne

Właściwości Kotlin zadeklarowane w nazwanym obiekcie lub obiekcie towarzyszącym będą miały statyczne pola zapasowe w tym nazwanym obiekcie lub w klasie zawierającej obiekt towarzyszący.

Zwykle te pola są prywatne, ale można je ujawnić w jeden z następujących sposobów:

  • @JvmField adnotacja;
  • lateinit modyfikator;
  • const modyfikator.

Więcej szczegółów tutaj - https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields

Anoop M.
źródło
4

Coś, o czym nie ma mowy w żadnej z odpowiedzi, to koszt użytkowania companion objects. Jak możesz przeczytać tutaj , obiekty towarzyszące są w rzeczywistości obiektami i tworzenie ich pochłania zasoby. Ponadto może być konieczne przejście przez więcej niż jedną funkcję pobierającą za każdym razem, gdy używasz stałej. Jeśli wszystko, czego potrzebujesz, to kilka prymitywnych stałych, prawdopodobnie lepiej będzie po prostu użyć, valaby uzyskać lepszą wydajność i uniknąć companion object.

TL; DR; artykułu:

Użycie obiektu towarzyszącego faktycznie zmienia ten kod

class MyClass {

    companion object {
        private val TAG = "TAG"
    }

    fun helloWorld() {
        println(TAG)
    }
}

W tym kodzie:

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

Więc staraj się ich unikać.

Sir Codesalot
źródło
3

stałe lokalne:

const val NAME = "name"

Stałe globalne:

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

dostęp do MyConstants.NAME

Amjed Baig
źródło
1

Istnieje kilka sposobów definiowania stałych w Kotlinie,

Korzystanie z obiektu towarzyszącego

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

możesz użyć powyższego bloku obiektu towarzyszącego w dowolnej klasie i zdefiniować wszystkie pola wewnątrz tego bloku. Ale jest problem z tym podejściem, mówi dokumentacja:

mimo że elementy składowe obiektów towarzyszących wyglądają jak statyczne elementy członkowskie w innych językach, w czasie wykonywania są one nadal elementami składowymi instancji rzeczywistych obiektów i mogą na przykład implementować interfejsy.

Kiedy tworzysz swoje stałe za pomocą obiektu towarzyszącego i widzisz zdekompilowany kod bajtowy , zobaczysz coś takiego jak poniżej,

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

Z tego miejsca możesz łatwo zobaczyć, co mówi dokumentacja, mimo że elementy składowe obiektów towarzyszących wyglądają jak statyczne elementy członkowskie w innych językach, w czasie wykonywania nadal są to elementy instancji rzeczywistych obiektów. Wykonuje dodatkową pracę niż jest to wymagane.

Teraz jest inny sposób, w którym nie musimy używać obiektu towarzyszącego, jak poniżej,

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

Ponownie, jeśli zobaczysz zdekompilowaną wersję kodu bajtowego powyższego fragmentu, znajdziesz coś takiego,

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

Teraz, jeśli widzisz powyższy zdekompilowany kod, tworzy on metodę get dla każdej zmiennej. Ta metoda pobierania nie jest w ogóle wymagana.

Aby pozbyć się tych metod get , powinieneś użyć const przed val, jak poniżej,

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

Teraz, jeśli zobaczysz zdekompilowany kod powyższego fragmentu, będzie on łatwiejszy do odczytania, ponieważ zapewnia najmniejszą konwersję kodu w tle.

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

    private ApiConstants() {
     }

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

Jest to więc najlepszy sposób tworzenia stałych.

Abhishek Kumar
źródło
0

Dla prymitywów i ciągów:

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

W innych przypadkach:

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

Przykład:

/*
 * 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
źródło