Konstanta di Kotlin - apa cara yang disarankan untuk membuatnya?

165

Bagaimana direkomendasikan untuk membuat konstanta di Kotlin? Dan apa konvensi penamaannya? Saya belum menemukan itu di dokumentasi.

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

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

Atau ...?

Jodimoro
sumber
4
Jika Anda menginginkan sesuatu yang berhubungan dengan public static finalbidang di Jawa, gunakan const valobjek pendamping Anda. Jika Anda ingin private static finalbidang dan pengambil publik, gunakan valobjek pendamping Anda.
Michael
2
Inilah blogpost yang menjelaskan cara mendefinisikan konstanta di Kotlin: blog.egorand.me/where-do-i-put-my-constants-in-kotlin
Micer
Periksa artikel ini . Ini memberikan gambaran bagus tentang berbagai cara di mana Anda dapat menyimpan konstanta Anda, dengan trade-off kinerja terkait.
firedrillsergeant

Jawaban:

132

Di Kotlin, jika Anda ingin membuat konstanta lokal yang seharusnya digunakan di kelas maka Anda dapat membuatnya seperti di bawah ini

val MY_CONSTANT = "Constants"

Dan jika Anda ingin membuat konstanta publik di kotlin seperti final statis publik di java, Anda dapat membuatnya sebagai berikut.

companion object{

     const val MY_CONSTANT = "Constants"

}
AaRiF
sumber
3
Bagaimana saya menggunakannya dalam file terpisah seperti nama file baru Constants.ktatau bagaimana?
Naveed Abbas
2
saya menggunakan file untuk konstanta. simpan semua konstanta saya di sana.
filthy_wizard
2
Anda tidak perlu companion objectjawaban Ipiotrpo yang saya pikir harus diterima
Chiara
@Chiara objek pendamping (dan kelas terlampir) berfungsi sebagai namespace, yang bertentangan dengan deklarasi tingkat atas. Saya pikir kedua jawaban bisa masuk akal tergantung pada situasinya.
jingx
@jingx ya, Anda ada benarnya di sana untuk menambahkan namespace, Anda membutuhkannya. : +1:
Chiara
118

Hindari menggunakan benda pengiring. Di balik tenda, metode instance pengambil dan penyetel dibuat agar bidang dapat diakses. Memanggil metode instance secara teknis lebih mahal daripada memanggil metode statis.

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

Alih-alih mendefinisikan konstanta di object.

Latihan yang disarankan :

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

dan mengaksesnya secara global seperti ini: DbConstants.TABLE_USER_ATTRIBUTE_EMPID

sudesh
sumber
Bukankah objek pendamping merupakan kasus khusus dari suatu objek? Bagaimana a const valdalam objek pengiring dapat berbeda dari const valdalam objek biasa (yaitu satu-satunya perbedaan antara contoh Anda tampaknya adalah bahwa Anda dihilangkan constdalam case objek pengiring - jika Anda menambahkan const, contoh harus memiliki kinerja yang sama)
Erwin Bolwidt
1
@ErwinBolwidt Saya pikir titik @ sudesh adalah bahwa seseorang tidak boleh menggunakan desain kelas-pembungkus-objek-pendamping ketika satu-satunya tujuan struktur adalah untuk menyediakan namespace untuk beberapa nilai konstan. Tetapi jika struktur Anda perlu instantiable dan juga melampirkan beberapa const vals, menyatakan companion objectbenar.
Ari Lacenski
7
@ ErwinBolwidt: sudesh benar, bytecode yang dihasilkan untuk objek pengiring melibatkan pembuatan objek tambahan dengan getter di bawah tenda. Untuk penjelasan yang bagus dengan contoh kotlin terurai,
dominik
2
terima kasih @dominik, ini adalah artikel yang sangat rinci, saya merekomendasikan ini kepada semua orang yang ingin memahami ini secara mendalam, ada banyak kasus di mana kotlin menghasilkan bytecode suboptimal, jetbrains telah menyelesaikan banyak bug terkait kinerja seperti itu ... mengawasi diskusi .kotlinlang.org , Anda akan diberi tahu tentang banyak aspek mendasar tersebut.
sudesh
1
Saya telah belajar banyak dari jawaban Anda hari ini @sudesh terima kasih!
Rakhi Dhavale
34

Pertama-tama , konvensi penamaan di Kotlin untuk konstanta sama dengan di java (mis: MY_CONST_IN_UPPERCASE).

Bagaimana saya membuatnya?

1. Sebagai nilai tingkat atas (disarankan)

Anda hanya perlu meletakkan const Anda di luar deklarasi kelas Anda.

Dua kemungkinan : Nyatakan const Anda di file kelas Anda (const Anda memiliki hubungan yang jelas dengan kelas Anda)

private const val CONST_USED_BY_MY_CLASS = 1

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

Buat file constants.kt khusus tempat menyimpan const global itu (Di sini Anda ingin menggunakan const Anda secara luas di seluruh proyek Anda):

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

Maka Anda hanya perlu mengimpornya di tempat yang Anda perlukan:

import com.project.constants

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

2. Deklarasikan dalam objek pengiring (atau deklarasi objek)

Ini jauh lebih bersih karena di bawah kap, ketika bytecode dihasilkan, objek yang tidak berguna dibuat:

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

Lebih buruk lagi jika Anda mendeklarasikannya sebagai val daripada const (kompiler akan menghasilkan objek yang tidak berguna + fungsi yang tidak berguna):

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

Catatan :

Di kotlin, const hanya bisa menampung tipe primitif. Jika Anda ingin meneruskan fungsi ke dalamnya, Anda perlu menambahkan penjelasan @JvmField. Pada waktu kompilasi, itu akan berubah sebagai variabel final statis publik. Tapi ini lebih lambat daripada dengan tipe primitif. Cobalah menghindarinya.

@JvmField val foo = Foo()
A.Mode
sumber
ini harus menjadi jawaban yang diterima. Lagi pula dalam kasus seperti: Pola final public static REGEX_NOTEMPTY = Pattern.compile (". +") ????
Xan
24

Nilai yang diketahui pada waktu kompilasi dapat (dan menurut saya harus) ditandai sebagai konstan.

Konvensi penamaan harus mengikuti yang Java dan harus terlihat dengan benar ketika digunakan dari kode Java (entah bagaimana sulit untuk dicapai dengan objek pendamping, tetapi bagaimanapun juga).

Deklarasi konstan yang tepat adalah:

const val MY_CONST = "something"
const val MY_INT = 1
piotrpo
sumber
3
Naming conventions should follow Java ones- mengapa
Jodimoro
3
Kotlin biasanya mengikuti konvensi Java secara default, jika tidak ditentukan sebaliknya, untuk membuat interop lancar.
zsmb13
4
Ditentukan seperti itu dalam dokumentasi @Jodimoro kotlinlang.org/docs/reference/coding-conventions.html
Neil
2
@ Neil, bukan.
Jodimoro
13
Dalam tautan itu saya memposting kata merekaIf in doubt, default to the Java Coding Conventions
Neil
16

Anda tidak perlu kelas, objek atau objek pendamping untuk mendeklarasikan konstanta di Kotlin. Anda bisa mendeklarasikan file yang memegang semua konstanta (misalnya Constants.kt atau Anda juga bisa meletakkannya di dalam file Kotlin yang ada) dan secara langsung mendeklarasikan konstanta di dalam file. Konstanta yang diketahui pada waktu kompilasi harus ditandai dengan const.

Jadi, dalam hal ini, seharusnya:

const val MY_CONST = "something"

dan kemudian Anda dapat mengimpor konstanta menggunakan:

import package_name.MY_CONST

Anda bisa merujuk ke tautan ini

Abdul Wadood
sumber
13
Konstanta harus berada di kelas yang terkait dengan mereka. Jika Anda membuat kelas 'Konstanta' Anda akan berakhir, akhirnya, akan ada ratusan konstanta di dalamnya. Pe: MAX_WIDTH, MAX_HEIGHT harus berada di kelas Layar sehingga Anda dapat mengaksesnya secara logis: Screen.MAX_WIDTH dan Anda tidak perlu meletakkan Konstanta.SCREEN_MAX_WIDTH yang akan diduplikasi dengan Constants.SCR_MAX_W dan Constants.MAX_WIDTH dalam 2 tahun karena NOBODY gulir ratusan / thousans garis ke bawah ketika mereka menekan Ctrl + spasi untuk melengkapi otomatis. Serius: jangan lakukan itu. mengarah ke unmaintainability
inigoD
1
@ InigoD Itu benar jika Anda menggunakan konstanta di satu tempat atau hanya pada anak-anak, tetapi ini jarang terjadi. Jika Anda meletakkan konstanta di kelas yang tidak jelas maka Anda melupakannya atau lebih mungkin Anda mengambil alih basis kode, Anda bisa menduplikatnya. Atau tidak jelas di mana menempatkannya. Sumber atau tujuannya? Anda dapat membuat beberapa file konstan, yang mudah ditemukan. Satu untuk kunci preferensi, satu untuk kunci permintaan, satu untuk konstanta tampilan, dll.
Herrbert74
1
@ Herrbert74 Saya minta maaf tapi saya harus tidak setuju dengan Anda. Saya setuju bahwa kadang-kadang sulit untuk menemukan yang mana, tetapi tempat yang konstan harus selalu menjadi kelas yang lebih terkait dengannya. Dan menyimpannya secara acak dalam file angka acak bukanlah cara terbaik jika Anda ingin mengambilnya nanti ... Anda akan berargumen bahwa mereka tidak akan disimpan secara acak tetapi dalam paket yang terkait dengan konstanta, tapi itu hanya alasan untuk tidak menempatkan mereka di kelas yang terkait dengan mereka, yang pada akhirnya, tempat mereka ...
inigoD
4
Jika sebuah konstanta benar-benar global atau memiliki cakupan besar ... seperti nilai untuk anotasi yang digunakan di semua paket, atau nama Header yang diambil oleh beberapa Pengontrol, dll, maka sepenuhnya dapat diterima untuk membuat konstanta " kelas "yang dicakup secara tepat. Namun, konstanta yang hanya digunakan dalam konteks tertentu, harus mencakup konteks itu, dan dideklarasikan di kelas yang relevan.
Nephthys76
@ Nephthys76 Sama seperti catatan, untuk " seperti nilai untuk anotasi yang digunakan di semua paket " secara khusus, saya akan mengatakan tempat terbaik untuk konstanta adalah di kelas anotasi.
Slaw
8

Jika Anda meletakkan const val valName = valValuesebelum nama kelas, cara ini akan membuat a

public static final YourClass.Ktyang akan memiliki public static finalnilai.

Kotlin :

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

Java didekompilasi:

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
sumber
Apakah ini benar? Adakah yang punya pengalaman dengan metode ini?
Scott Biggs
5
class Myclass {

 companion object {
        const val MYCONSTANT = 479
}

Anda memiliki dua pilihan, Anda dapat menggunakan constkata kunci atau menggunakan @JvmFieldyang membuatnya menjadi konstanta akhir statis java.

class Myclass {

     companion object {
           @JvmField val MYCONSTANT = 479
    }

Jika Anda menggunakan @JvmFieldanotasi maka setelah dikompilasi konstanta akan dimasukkan untuk Anda seperti yang Anda sebut dalam java.
Sama seperti Anda akan menyebutnya dalam java kompiler akan menggantikannya untuk Anda ketika Anda memanggil konstanta pengiring dalam kode.

Namun, jika Anda menggunakan kata kunci const maka nilai konstanta menjadi inline. Secara inline maksud saya nilai aktual digunakan setelah dikompilasi.

jadi untuk meringkas di sini adalah apa yang kompiler akan lakukan untuk Anda:

//so for @JvmField:

Foo var1 = Constants.FOO;

//and for const:

Foo var1 = 479
j2emanue
sumber
5

Nilai & metode statis dan konstan diumumkan

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
}

}

Nilai akses di mana saja

val value = MyConstant.PI
val value = MyConstant.sumValue(10,5)
Shomu
sumber
1
bagaimana cara mendefinisikan metode global atau statis?
Samad Talukder
@SamadTalukder Di Kotlin akan menyenangkan sumValue (v1: Int, v2: Int): Int {return v1 + v2}
Shomu
5

Seperti val, variabel yang didefinisikan dengan constkata kunci tidak dapat diubah. Perbedaannya di sini adalah yang constdigunakan untuk variabel yang dikenal pada waktu kompilasi.

Mendeklarasikan variabel constsangat mirip dengan menggunakan statickata kunci di Jawa.

Mari kita lihat bagaimana mendeklarasikan variabel const di Kotlin:

const val COMMUNITY_NAME = "wiki"

Dan kode analog yang ditulis dalam Java adalah:

final static String COMMUNITY_NAME = "wiki";

Menambah jawaban di atas -

@JvmField digunakan untuk menginstruksikan kompiler Kotlin untuk tidak menghasilkan getter / setter untuk properti ini dan mengeksposnya sebagai bidang.

 @JvmField
 val COMMUNITY_NAME: "Wiki"

Bidang statis

Properti Kotlin yang dideklarasikan dalam objek bernama atau objek pendamping akan memiliki bidang dukungan statis baik di objek yang disebutkan atau di kelas yang berisi objek pendamping.

Biasanya bidang ini bersifat pribadi tetapi dapat diekspos dengan salah satu cara berikut:

  • @JvmField anotasi;
  • lateinit pengubah;
  • const pengubah.

Lebih detail di sini - https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields

Anoop M
sumber
4

Sesuatu yang tidak disebutkan dalam jawaban mana pun adalah kelebihan penggunaan companion objects. Seperti yang dapat Anda baca di sini , objek pengiring sebenarnya adalah objek dan membuatnya menghabiskan sumber daya. Selain itu, Anda mungkin harus melalui lebih dari satu fungsi pengambil setiap kali Anda menggunakan konstanta Anda. Jika yang Anda butuhkan hanyalah beberapa konstanta primitif, Anda mungkin akan lebih baik menggunakan valuntuk mendapatkan kinerja yang lebih baik dan menghindari companion object.

TL; DR; artikel:

Menggunakan objek pengiring sebenarnya mengubah kode ini

class MyClass {

    companion object {
        private val TAG = "TAG"
    }

    fun helloWorld() {
        println(TAG)
    }
}

Ke dalam kode ini:

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

Jadi cobalah untuk menghindarinya.

Tuan Codesalot
sumber
3

konstanta lokal:

const val NAME = "name"

Konstanta global:

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

akses MyConstants.NAME

Amjed Baig
sumber
1

Ada beberapa cara untuk mendefinisikan konstanta di Kotlin,

Menggunakan objek pendamping

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

Anda bisa menggunakan blok objek pengiring di atas di dalam kelas apa pun dan mendefinisikan semua bidang Anda di dalam blok ini sendiri. Tetapi ada masalah dengan pendekatan ini, dokumentasi mengatakan,

meskipun anggota objek pendamping terlihat seperti anggota statis dalam bahasa lain, pada saat runtime mereka masih menjadi anggota objek nyata, dan dapat, misalnya, mengimplementasikan antarmuka.

Saat Anda membuat konstanta menggunakan objek pengiring, dan melihat bytecode yang di-decompile , Anda akan menemukan sesuatu seperti di bawah ini,

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

Dari sini Anda dapat dengan mudah melihat apa yang dikatakan dokumentasi, meskipun anggota objek pendamping terlihat seperti anggota statis dalam bahasa lain, pada saat runtime mereka masih menjadi contoh anggota objek nyata. Ini melakukan pekerjaan ekstra daripada yang diperlukan.

Sekarang datang cara lain, di mana kita tidak perlu menggunakan objek pendamping seperti di bawah ini,

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

Sekali lagi jika Anda melihat versi kode byte byte yang di-dekompilasi, Anda akan menemukan sesuatu seperti ini,

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

Sekarang jika Anda melihat kode dekompilasi di atas, itu membuat metode get untuk setiap variabel. Metode get ini tidak diperlukan sama sekali.

Untuk menghilangkan metode get ini , Anda harus menggunakan const sebelum val seperti di bawah ini,

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

Sekarang jika Anda melihat kode yang dikompilasi dari cuplikan di atas, Anda akan lebih mudah membaca karena ia melakukan konversi latar belakang paling sedikit untuk kode Anda.

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

    private ApiConstants() {
     }

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

Jadi ini adalah cara terbaik untuk membuat konstanta.

Abhishek Kumar
sumber
0

Untuk primitif dan String:

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

Untuk kasus lain:

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

Contoh:

/*
 * 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
sumber