Các hằng số trong Kotlin - một cách được đề xuất để tạo ra chúng là gì?


165

Làm thế nào để tạo hằng số trong Kotlin? Và quy ước đặt tên là gì? Tôi đã không tìm thấy điều đó trong tài liệu.

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

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

Hoặc là ...?


4
Nếu bạn muốn một cái gì đó tương ứng với một public static finaltrường trong Java, hãy sử dụng const valtrong đối tượng đồng hành của bạn. Nếu bạn muốn một private static finaltrường và một getter công khai, hãy sử dụng valtrong đối tượng đồng hành của bạn.
Michael

2
Đây là blogpost giải thích các cách xác định hằng số trong Kotlin: blog.egorand.me/where-do-i-put-my-constants-in-kotlin
Micer

Kiểm tra bài viết này . Nó cung cấp một cái nhìn tổng quan đẹp về các cách khác nhau để bạn có thể lưu trữ các hằng số của mình, với sự đánh đổi hiệu suất liên quan.
firerillsergete

Câu trả lời:


130

Trong Kotlin, nếu bạn muốn tạo các hằng số cục bộ được cho là được sử dụng trong lớp thì bạn có thể tạo nó như bên dưới

val MY_CONSTANT = "Constants"

Và nếu bạn muốn tạo một hằng công khai trong kotlin như chung kết tĩnh trong java, bạn có thể tạo nó như sau.

companion object{

     const val MY_CONSTANT = "Constants"

}

3
Làm thế nào tôi có thể sử dụng nó trong một tệp riêng biệt như một tệp mới có tên Constants.kthoặc làm thế nào?
Naveed Abbas

2
tôi sử dụng một tập tin cho hằng số. giữ tất cả các hằng số của tôi trong đó
filthy_wizard

2
bạn không cần companion objectcâu trả lời Tôi nghĩ @piotrpo nên được chấp nhận
Chiara

@Chiara đối tượng đồng hành (và lớp kèm theo của nó) đóng vai trò là không gian tên, trái ngược với khai báo cấp cao nhất. Tôi nghĩ rằng cả hai câu trả lời có thể có ý nghĩa tùy thuộc vào tình huống.
jingx 19/12/18

@jingx vâng, bạn có một điểm ở đó để thêm một không gian tên cho nó bạn cần nó. : +1:
Chiara

118

Tránh sử dụng các đối tượng đồng hành. Đằng sau mui xe, các phương thức cá thể getter và setter được tạo cho các trường có thể truy cập được. Gọi các phương thức thể hiện đắt hơn về mặt kỹ thuật so với gọi các phương thức tĩnh.

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

Thay vào đó xác định các hằng số trong object.

Khuyến nghị thực hành :

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

và truy cập chúng trên toàn cầu như thế này: DbConstants.TABLE_USER_ATTRIBUTE_EMPID


Không phải là một đối tượng đồng hành là một trường hợp đặc biệt của một đối tượng? Làm thế nào một const valđối tượng đồng hành có thể khác với một const valđối tượng bình thường (nghĩa là sự khác biệt duy nhất giữa các ví dụ của bạn dường như là bạn đã bỏ qua consttrong trường hợp đối tượng đồng hành - nếu bạn thêm const, các ví dụ sẽ có cùng hiệu suất)
Erwin Bolwidt

1
@ErwinBolwidt Tôi nghĩ rằng quan điểm của @ sudesh là người ta không nên sử dụng thiết kế đối tượng đồng hành lớp-lớp khi mục đích duy nhất của cấu trúc là cung cấp một không gian tên cho một số giá trị không đổi. Nhưng nếu cấu trúc của bạn cần có thể thực hiện được và cũng kèm theo một vài const vals, khai báo a companion objectlà chính xác.
Ari Lacenski

7
@ErwinBolwidt: sudesh là đúng, được tạo bởi mã byte cho các đối tượng đồng hành liên quan đến việc tạo đối tượng bổ sung với getters dưới mui xe. Để được giải thích tốt với các ví dụ về kotlin đã dịch ngược, hãy xem blog.egorand.me/where-do-i-put-my-constants-in-kotlin
dominik

2
cảm ơn @dominik, đây là một bài viết rất chi tiết, tôi giới thiệu nó cho tất cả những ai muốn hiểu sâu về vấn đề này, có nhiều trường hợp kotlin tạo ra mã byte tối ưu, jetbrains đã giải quyết nhiều lỗi liên quan đến hiệu suất như vậy ... hãy chú ý thảo luận .kotlinlang.org , bạn sẽ được thông báo về nhiều khía cạnh cơ bản như vậy.
sudesh

1
Tôi đã học được nhiều điều từ câu trả lời của bạn ngày hôm nay @sudesh cảm ơn!
Rakhi Dhavale

34

Trước hết , quy ước đặt tên trong Kotlin cho các hằng số giống với trong java (ví dụ: MY_CONST_IN_UPPERCASE).

Tôi nên tạo nó như thế nào?

1. Là một giá trị cấp cao nhất (được khuyến nghị)

Bạn chỉ cần đặt const của bạn bên ngoài khai báo lớp học của bạn.

Hai khả năng : Khai báo const của bạn trong tệp lớp của bạn (const của bạn có mối quan hệ rõ ràng với lớp của bạn)

private const val CONST_USED_BY_MY_CLASS = 1

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

Tạo một tệp constants.kt chuyên dụng để lưu trữ các const toàn cầu đó (Ở đây bạn muốn sử dụng const của bạn rộng rãi trong dự án của bạn):

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

Sau đó, bạn chỉ cần nhập nó vào nơi bạn cần:

import com.project.constants

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

2. Khai báo nó trong một đối tượng đồng hành (hoặc khai báo đối tượng)

Điều này ít sạch hơn nhiều vì dưới mui xe, khi mã byte được tạo, một đối tượng vô dụng được tạo:

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

Thậm chí tệ hơn nếu bạn khai báo nó như một val thay vì const (trình biên dịch sẽ tạo ra một đối tượng vô dụng + một hàm vô dụng):

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

Ghi chú :

Trong kotlin, const chỉ có thể giữ các kiểu nguyên thủy. Nếu bạn muốn truyền một hàm cho nó, bạn cần thêm chú thích @JvmField. Tại thời điểm biên dịch, nó sẽ được chuyển đổi thành biến cuối cùng tĩnh công khai. Nhưng nó chậm hơn so với kiểu nguyên thủy. Cố gắng tránh nó

@JvmField val foo = Foo()

đây sẽ là câu trả lời được chấp nhận dù sao trong một trường hợp như: Mẫu cuối cùng tĩnh công khai REGEX_NOTEMPTY = Pattern.compile (". +") ????
Xan

23

Các giá trị được biết tại thời điểm biên dịch có thể (và theo tôi nên) được đánh dấu là hằng số.

Các quy ước đặt tên phải tuân theo các quy ước Java và phải được hiển thị chính xác khi được sử dụng từ mã Java (bằng cách nào đó khó đạt được với các đối tượng đồng hành, nhưng dù sao đi nữa).

Các khai báo hằng thích hợp là:

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

3
Naming conventions should follow Java ones- tại sao?
Jodimoro

3
Theo mặc định, Kotlin thường tuân theo các quy ước Java, nếu không được chỉ định khác, để làm cho giao thoa trơn tru.
zsmb13

4
Được chỉ định như thế trong tài liệu @Jodimoro kotlinlang.org/docs/reference/coding-conventions.html
Neil

2
@ Không, không phải vậy.
Jodimoro

13
Trong liên kết đó tôi đã đăng họ nóiIf in doubt, default to the Java Coding Conventions
Neil

16

Bạn không cần một lớp, một đối tượng hoặc một đối tượng đồng hành để khai báo các hằng số trong Kotlin. Bạn chỉ có thể khai báo một tệp chứa tất cả các hằng số (ví dụ: Constants.kt hoặc bạn cũng có thể đặt chúng bên trong bất kỳ tệp Kotlin hiện có nào) và trực tiếp khai báo các hằng số bên trong tệp. Các hằng số đã biết tại thời điểm biên dịch phải được đánh dấu bằng const.

Vì vậy, trong trường hợp này, nó phải là:

const val MY_CONST = "something"

và sau đó bạn có thể nhập hằng số bằng cách sử dụng:

import package_name.MY_CONST

Bạn có thể tham khảo liên kết này


13
Các hằng số phải nằm trong lớp mà chúng có liên quan. Nếu bạn tạo một lớp 'Hằng số, bạn sẽ kết thúc, cuối cùng, sẽ có hàng trăm hằng số bên trong nó. Pe: MAX_WIDTH, MAX_HEIGHT phải nằm trong lớp Màn hình để bạn có thể truy cập vào nó một cách hợp lý: Screen.MAX_WIDTH và bạn không cần đặt Constants.SCREEN_MAX_WIDTH sẽ được nhân đôi với Constants.SCR_MAX_W cuộn hàng trăm / nghìn dòng xuống khi chúng đẩy Ctrl + dấu cách để tự động hoàn tất. Nghiêm túc: đừng làm điều đó. dẫn đến sự không thể nhận ra
inigoD

1
@inigoD Điều đó đúng nếu bạn sử dụng hằng số ở một nơi hoặc chỉ ở trẻ em, nhưng điều này hầu như không bao giờ xảy ra. Nếu bạn đặt hằng số trong một lớp tối nghĩa thì bạn sẽ quên nó hoặc nhiều khả năng bạn tiếp quản một cơ sở mã, bạn có thể sao chép chúng. Hoặc không rõ ràng để đặt chúng ở đâu. Nguồn hay đích đến? Bạn có thể tạo một số tệp không đổi, rất dễ tìm. Một cho các khóa ưu tiên, một cho các khóa yêu cầu, một cho các hằng số xem, v.v.
Herrbert74

1
@ Herrbert74 Tôi xin lỗi nhưng tôi không đồng ý với bạn. Tôi đồng ý rằng đôi khi có thể khó tìm thấy nó, nhưng một nơi không đổi phải luôn là lớp có liên quan nhiều hơn đến nó. Và lưu chúng ngẫu nhiên trong một số tệp ngẫu nhiên không phải là cách tốt nhất nếu bạn muốn truy xuất chúng sau này ... Bạn sẽ lập luận rằng chúng sẽ không được lưu trữ ngẫu nhiên mà trong các gói có liên quan đến, nhưng đó chỉ là một lý do để không đặt chúng vào các lớp mà chúng có liên quan, cuối cùng, vị trí của chúng ...
inigoD

4
Nếu một hằng số thực sự toàn cầu hoặc có phạm vi lớn ... chẳng hạn như giá trị của chú thích được sử dụng trên tất cả các gói hoặc tên Tiêu đề đang được nhiều Bộ điều khiển tìm nạp, v.v., thì việc tạo ra một "hằng số là hoàn toàn chấp nhận được lớp "đó là phạm vi thích hợp. Tuy nhiên, các hằng số chỉ được sử dụng trong các ngữ cảnh cụ thể, nên được đặt trong phạm vi bối cảnh đó và được khai báo trong lớp có liên quan.
Nephthys76

@ Nephthys76 Cũng như một ghi chú, đối với " chẳng hạn như giá trị của chú thích được sử dụng trên tất cả các gói ", tôi sẽ nói rằng vị trí tốt nhất cho hằng số là trong lớp chú thích.
Slaw

8

Nếu bạn đặt const val valName = valValuetrước tên lớp, theo cách này, nó sẽ tạo ra một

public static final YourClass.KtĐiều đó sẽ có các public static finalgiá trị.

Kotlin :

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

Dịch ngược Java:

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

Điều này có đúng không? Bất cứ ai cũng có kinh nghiệm với phương pháp này?
Scott Biggie

5
class Myclass {

 companion object {
        const val MYCONSTANT = 479
}

bạn có hai lựa chọn bạn có thể sử dụng consttừ khóa hoặc sử dụng từ khóa @JvmFieldnày làm cho hằng số tĩnh cuối cùng của java.

class Myclass {

     companion object {
           @JvmField val MYCONSTANT = 479
    }

Nếu bạn sử dụng @JvmFieldchú thích thì sau khi nó biên dịch, hằng số sẽ được đặt cho bạn theo cách bạn sẽ gọi nó trong java.
Giống như bạn sẽ gọi nó trong java, trình biên dịch sẽ thay thế nó cho bạn khi bạn gọi hằng số đồng hành trong mã.

Tuy nhiên, nếu bạn sử dụng từ khóa const thì giá trị của hằng số sẽ được nội tuyến. Theo dòng tôi có nghĩa là giá trị thực được sử dụng sau khi nó biên dịch.

Vì vậy, để tóm tắt ở đây là những gì trình biên dịch sẽ làm cho bạn:

//so for @JvmField:

Foo var1 = Constants.FOO;

//and for const:

Foo var1 = 479

5

Khai báo giá trị và phương thức tĩnh và không đổi của 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
}

}

Giá trị truy cập ở mọi nơi

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

1
Làm thế nào để xác định phương pháp toàn cầu hoặc tĩnh?
Samad Talukder

@SamadTalukder Trong Kotlin, nó sẽ rất vui sumValue (v1: Int, v2: Int): Int {return v1 + v2}
Shomu

5

Giống như val, các biến được xác định với consttừ khóa là bất biến. Sự khác biệt ở đây là constđược sử dụng cho các biến được biết tại thời gian biên dịch.

Khai báo một biến constgiống như sử dụng statictừ khóa trong Java.

Chúng ta hãy xem cách khai báo một biến const trong Kotlin:

const val COMMUNITY_NAME = "wiki"

Và mã tương tự được viết bằng Java sẽ là:

final static String COMMUNITY_NAME = "wiki";

Thêm vào các câu trả lời ở trên -

@JvmField một được sử dụng để hướng dẫn trình biên dịch Kotlin không tạo getters / setters cho thuộc tính này và hiển thị nó dưới dạng một trường.

 @JvmField
 val COMMUNITY_NAME: "Wiki"

Các trường tĩnh

Các thuộc tính của Kotlin được khai báo trong một đối tượng được đặt tên hoặc một đối tượng đồng hành sẽ có các trường sao lưu tĩnh trong đối tượng được đặt tên đó hoặc trong lớp có chứa đối tượng đồng hành.

Thông thường các trường này là riêng tư nhưng chúng có thể được hiển thị theo một trong các cách sau:

  • @JvmField chú thích;
  • lateinit bổ nghĩa;
  • const bổ nghĩa.

Thêm chi tiết tại đây - https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields


4

Một cái gì đó không được đề cập trong bất kỳ câu trả lời là chi phí sử dụng companion objects. Như bạn có thể đọc ở đây , các đối tượng đồng hành trong thực tế là các đối tượng và tạo ra chúng tiêu tốn tài nguyên. Ngoài ra, bạn có thể cần phải trải qua nhiều hơn một hàm getter mỗi khi bạn sử dụng hằng số của mình. Nếu tất cả những gì bạn cần là một vài hằng số nguyên thủy, có lẽ bạn sẽ sử dụng tốt hơn valđể có được hiệu suất tốt hơn và tránh điều đó companion object.

TL; DR; của bài viết:

Sử dụng đối tượng đồng hành thực sự biến mã này

class MyClass {

    companion object {
        private val TAG = "TAG"
    }

    fun helloWorld() {
        println(TAG)
    }
}

Vào mã này:

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

Vì vậy, hãy cố gắng tránh chúng.


3

hằng số địa phương:

const val NAME = "name"

Hằng số toàn cầu:

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

truy cập MyConstants.NAME


1

Có một số cách bạn có thể định nghĩa các hằng số trong Kotlin,

Sử dụng đối tượng đồng hành

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

bạn có thể sử dụng khối đối tượng đồng hành ở trên bên trong bất kỳ lớp nào và xác định tất cả các trường của bạn bên trong khối này. Nhưng có một vấn đề với cách tiếp cận này, tài liệu nói,

mặc dù các thành viên của các đối tượng đồng hành trông giống như các thành viên tĩnh trong các ngôn ngữ khác, trong thời gian chạy, chúng vẫn là các thành viên thể hiện của các đối tượng thực và, ví dụ, có thể thực hiện các giao diện.

Khi bạn tạo các hằng số của mình bằng cách sử dụng đối tượng đồng hành và xem mã byte được dịch ngược , bạn sẽ có một cái gì đó như bên dưới,

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

Từ đây, bạn có thể dễ dàng thấy những gì tài liệu nói, mặc dù các thành viên của các đối tượng đồng hành trông giống như các thành viên tĩnh trong các ngôn ngữ khác, trong thời gian chạy, chúng vẫn là các thành viên thể hiện của các đối tượng thực. Nó làm việc thêm so với yêu cầu.

Bây giờ đến một cách khác, nơi chúng ta không cần sử dụng đối tượng đồng hành như bên dưới,

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

Một lần nữa nếu bạn thấy phiên bản dịch ngược của mã byte của đoạn mã trên, bạn sẽ tìm thấy một cái gì đó như thế này,

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

Bây giờ nếu bạn thấy mã dịch ngược ở trên, nó sẽ tạo phương thức get cho mỗi biến. Phương pháp get này là không cần thiết ở tất cả.

Để thoát khỏi các phương thức get này , bạn nên sử dụng const trước val như bên dưới,

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

Bây giờ nếu bạn thấy mã được dịch ngược của đoạn trích trên, bạn sẽ thấy dễ đọc hơn vì nó thực hiện chuyển đổi nền ít nhất cho mã của bạn.

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

    private ApiConstants() {
     }

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

Vì vậy, đây là cách tốt nhất để tạo hằng.


0

Đối với nguyên thủy và Chuỗi:

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

Đối với các trường hợp khác:

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

Thí dụ:

/*
 * 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!"
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.