Kotlin中的常量——推荐用什么方法来创建它们?

建议如何在Kotlin中创建常量?命名规则是什么?我在文档里没有找到。

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


//2
const val MY_CONST = "something"


//3
val myConst = "something"
}

或者…?

260682 次浏览

在编译时已知的值可以(并且在我看来应该)被标记为常量。

命名约定应该遵循Java约定,并且在从Java代码中使用时应该正确可见(这在某种程度上很难通过伴生对象实现,但无论如何)。

正确的常量声明是:

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

在Kotlin中,如果你想创建在类中使用的局部常数,那么你可以像下面这样创建它

val MY_CONSTANT = "Constants"

如果你想在kotlin中创建一个公共常量,比如java中的公共静态final,你可以按照下面的方法创建它。

companion object{


const val MY_CONSTANT = "Constants"


}

当地常量:

const val NAME = "name"

全局常量:

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

访问MyConstants。的名字

class Myclass {


companion object {
const val MYCONSTANT = 479
}
}

你有两个选择,你可以使用const关键字或使用@JvmField,这使它成为java的静态final常量。

class Myclass {


companion object {
@JvmField val MYCONSTANT = 479
}
}

如果你使用@JvmField注释,那么在它编译之后,常量就会以你在java中调用它的方式为你放入。
就像你在java中调用它一样,当你在代码中调用同伴常量时,编译器会为你替换它

但是,如果使用const关键字,则常量的值将内联。内联是指在编译后使用实际值。

所以总结一下编译器将为你做什么:

//so for @JvmField:


Foo var1 = Constants.FOO;


//and for const:


Foo var1 = 479

在Kotlin中声明常量不需要类、对象或伴生对象。你可以只声明一个包含所有常量的文件(例如Constants.kt,或者你也可以把它们放在任何现有的Kotlin文件中),并直接在文件中声明常量。编译时已知的常量必须用const标记。

所以,在这种情况下,它应该是:

const val MY_CONST = "something"

然后你可以导入常量使用:

import package_name.MY_CONST

你可以引用这个链接

在任何答案中都没有提到的是使用companion objects的开销。正如你可以读取在这里,伴生对象实际上是对象,创建它们会消耗资源。此外,每次使用常量时,可能需要使用多个getter函数。如果你所需要的只是类的几个实例上的几个基本常量,你可能最好使用val来获得更好的性能,而避免companion object。如果你的类有很多实例,那么代价就是更高的内存消耗,所以每个人都应该自己做决定。

TL,博士;文章:

使用伴侣对象实际上会将以下Kotlin代码:

class MyClass {


companion object {
private val TAG = "TAG"
}


fun helloWorld() {
println(TAG)
}
}

在这段Java代码中:

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

避免使用伴随对象。在幕后,为字段创建了getter和setter实例方法以供访问。从技术上讲,调用实例方法比调用静态方法代价更大。

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

相反,在object中定义常量。

推荐的做法:

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

并像这样全局访问它们: DbConstants.TABLE_USER_ATTRIBUTE_EMPID < / p >

如果你把const val valName = valValue放在类名之前,这样就会创建一个

public static final YourClass.Kt将具有public static final值。

芬兰湾的科特林:

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

Java反编译:

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

对于基元和字符串:

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

其他情况:

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

例子:

/*
* 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!"
}
}
}

首先, Kotlin中常量的命名约定与java中相同(例如:MY_CONST_IN_UPPERCASE)。

我应该如何创建它?

1. 作为顶级值(推荐)

你只需要把const 放在你的类声明中。

两种可能性:在你的类文件中声明你的const(你的const与你的类有明确的关系)

private const val CONST_USED_BY_MY_CLASS = 1


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

创建一个专用的常量。在这里你想要在你的项目中广泛使用你的const文件:

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

然后你只需要把它导入到你需要的地方:

import com.project.constants


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

2. 在伴生对象(或对象声明)中声明它

这是更干净,因为在底层,当字节码生成时,创建了一个无用的对象:

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

更糟糕的是如果你声明它为val而不是const(编译器将生成一个无用的对象+一个无用的函数):

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

注意:

在kotlin中,const只能保存原始类型。如果希望将调用函数的结果分配给它,则需要添加@JvmField注释。在编译时,它将被转换为一个公共静态final变量。但是它比原始类型要慢。尽量避免。

@JvmField val foo = Foo()

在Kotlin中有几种定义常量的方法,

使用伴随对象

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

你可以在任何类中使用上面的同伴对象块,并在这个块中定义你的所有字段。但是这种方法有一个问题,文档说,

尽管伴随对象的成员看起来像其他语言中的静态成员,但在运行时,它们仍然是实际对象的实例成员,并且可以实现接口。

当你使用同伴对象创建常量,并看到反编译字节码时,你会像下面这样:

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

从这里你可以很容易地看到文档说,尽管伴随对象的成员看起来像其他语言中的静态成员,但在运行时,它们仍然是实际对象的实例成员这是做额外的工作比要求。

现在有另一种方式,我们不需要使用伴随对象如下所示,

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

同样,如果你看到上述片段的字节代码的反编译版本,你会发现这样的东西,

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

现在,如果您看到上面的反编译代码,它正在为每个变量创建get方法。这个get方法根本不是必需的。

来摆脱这些get方法,你应该在瓦尔之前使用常量,如下所示,

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

现在,如果您看到上述代码片段的反编译代码,您会发现它更容易阅读,因为它为您的代码进行了最少的后台转换。

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


private ApiConstants() {
}


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

所以这是创建常数的最好方法。

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
}

在任何地方访问值

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

val一样,用const关键字定义的变量是不可变的。不同之处在于const用于编译时已知的变量。

声明变量const很像在Java中使用static关键字。

让我们看看如何在Kotlin中声明一个const变量:

const val COMMUNITY_NAME = "wiki"

用Java编写的类似代码是:

final static String COMMUNITY_NAME = "wiki";

加上上面的答案-

@JvmField可以用来指示Kotlin编译器不生成

 @JvmField
val COMMUNITY_NAME = "Wiki"

静态字段

在命名对象或伴随对象中声明的Kotlin属性 将有静态支持字段,无论是在该命名对象或 包含伴生对象的类

通常这些字段是私有的,但它们可以通过以下方式之一公开:

  • @JvmField注释;
  • lateinit修饰语;
  • const修饰符。

更多细节在这里- https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields

我认为这将是把一个包的所有常量放在同一个文件中的最好方法,正如在其他答案中提到的,这避免了创建伴侣对象,这使得这个性能和非常类似于Java constants类。

class Constants {
object Analytics {
const val PROJECT_OPEN = "project_open"
const val PROJECT_CLOSE = "project_close"
}


object HTTP {
const val BASE_URL = "x.y.com"
}


object DBConst {
const val TABLE_NAME = "abc"
}
}

这可以从这样的代码中引用,使其非常结构化。

Constants.Analytics.PROJECT_OPEN
Constants.HTTP.BASE_URL
Constants.DBConst.TABLE_NAME
在Kotlin中,当我们声明变量时,我们有两个选项。'var'或'val'。 遵循变量的命名惯例,我认为我们可以简单地生成“常量”,这意味着我想要一个固定的值分配给特定的变量,就像下面的示例代码

private val tag =“mainactivity”;

我认为我们不需要像在Java世界中所做的那样,麻烦地使用大写字母来区分“常量”和变量。