Kotlin中的常量-创建它们的推荐方法是什么?


165

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

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

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

要么 ...?


4
如果需要与public static finalJava中的字段相对应的内容,请const val在您的伴随对象中使用。如果您想要一个private static final字段和一个公共获取器,请val在您的伴随对象中使用。
迈克尔(Michael)

2
这是解释在Kotlin中定义常量的方法的博客文章:blog.egorand.me/where-do-i-put-my-constants-in-kotlin
Micer,

查看这篇文章。它很好地概述了存储常量的不同方法以及相关的性能折衷。
firedrillsergeant

Answers:


132

在Kotlin中,如果要创建应该在该类中使用的局部常量,则可以如下创建

val MY_CONSTANT = "Constants"

如果您想在kotlin中创建一个公共常量,例如public static final在Java中创建可以如下创建它。

companion object{

     const val MY_CONSTANT = "Constants"

}

3
如何在单独的文件(如名为的新文件)中使用它,Constants.kt或者如何使用?
Naveed Abbas

2
我使用文件常量。保持我所有的常数在那里。
filthy_wizard

2
您不需要companion object我认为@piotrpo答案应该被接受
Chiara

与顶层声明相反,@ Chiara伴随对象(及其封闭类)用作名称空间。我认为这两个答案都可以视情况而定。
jingx

@jingx是的,您可以在此处添加所需的名称空间。:+1:
Chiara

118

避免使用伴随对象。在引擎盖后面,创建了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


伴随对象不是对象的特殊情况吗?如何能const val在同伴的对象是比任何不同const val在一个普通的对象(即你的例子之间的唯一差别似乎是你忽略const的同伴对象的情况下-如果添加const的例子应该具有相同的性能)
欧文Bolwidt

1
@ErwinBolwidt我认为@sudesh的观点是,当结构的唯一目的是为某些常量值提供名称空间时,不应使用class-wrapping-companion-object设计。但是,如果您的结构需要可实例化并且还包含几个const vals,则声明a companion object是正确的。
阿里·拉森斯基

7
@ErwinBolwidt:sudesh是正确的,为同伴对象生成的字节码涉及在幕后使用吸气剂进行额外的对象创建。有关反编译Kotlin示例的详细说明,请参见blog.egorand.me/where-do-i-put-my-constants-in-kotlin
dominik

2
谢谢@dominik,这是一篇非常详细的文章,我建议所有想要深入了解它的人阅读这篇文章,在许多情况下,kotlin产生次优字节码,jetbrains解决了许多此类与性能相关的错误...请继续关注讨论.kotlinlang.org,您将被告知许多此类潜在方面。
sudesh

1
我今天从您的回答中学到了很多东西@sudesh谢谢!
Rakhi Dhavale

34

首先, 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 
}

创建一个专用的constants.kt文件,在其中存储这些全局const(在这里您想在整个项目中广泛使用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批注。在编译时,它将被转换为公共静态最终变量。但是它比原始类型要慢。尽量避免它。

@JvmField val foo = Foo()

这应该是公认的答案。无论如何,例如:public static final Pattern REGEX_NOTEMPTY = Pattern.compile(“。+”)????
Xan

23

可以(我认为应该)将编译时已知的值标记为常量。

命名约定应遵循Java的约定,并且在从Java代码中使用时应正确可见(无论如何,伴随对象很难实现)。

正确的常量声明是:

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

3
Naming conventions should follow Java ones-为什么?
Jodimoro

3
Kotlin通常默认情况下遵循Java约定,如果没有另外指定,则使Interop平滑。
zsmb13


2
@Neil,不是。
乔迪莫罗

13
在该链接中,我发布了他们说If in doubt, default to the Java Coding Conventions
Neil

16

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

因此,在这种情况下,应为:

const val MY_CONST = "something"

然后可以使用以下命令导入常量:

import package_name.MY_CONST

你可以参考这个链接


13
常数必须在与它们相关的类中。如果您创建“常量”类,则最终将结束其中的数百个常量。Pe:MAX_WIDTH,MAX_HEIGHT必须在Screen类中,因此您可以逻辑地访问它:Screen.MAX_WIDTH,并且您不需要放置Constants.SCREEN_MAX_WIDTH,它将在两年内与Constants.SCR_MAX_W和Constants.MAX_WIDTH复制,因为NOBODY当他们按Ctrl + space自动完成时,向下滚动数百/千行。说真的:不要这样做。导致不
可维护性

1
@inigoD是正确的,如果您在一个地方或仅在儿童中使用常量,但是几乎没有这种情况。如果将常量放在晦涩的类中,则您会忘记它,或者更有可能接管代码库,则可以复制它们。或将它们放在哪里并不明显。源还是目的地?您可以创建几个易于查找的常量文件。一个用于首选项键,一个用于请求键,一个用于视图常数等
Herrbert74

1
@ Herrbert74对不起,但我不同意你的看法。我同意有时很难找到哪个位置,但是与它相关性更高的类应该始终位于一个恒定的位置。如果希望以后再检索它们,将它们随机保存在随机数文件中并不是最好的方法……您会争辩说,它们不会被随机存储,而是存储在与常量相关的包中,但这仅是一个借口不要将它们放在与之相关的类中,最后是它们的位置……
inigoD

4
如果常量是真正的全局常量或范围较大的常量(例如,在所有包中使用的注释的值,或由多个Controller获取的标头名称等),则完全可以接受创建“常量”类”的范围。但是,仅在特定上下文中使用的常量应在该上下文范围内使用,并在相关类中声明。
Nephthys76年8

@ Nephthys76就像一条注释,特别是对于“ 例如在所有软件包中使用的注释的值 ”,我想说该常量的最佳位置是在注释类中。
Slaw

8

如果你把 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

这是真的?有人对这种方法有经验吗?
Scott Biggs

5
class Myclass {

 companion object {
        const val MYCONSTANT = 479
}

您有两种选择,可以使用const关键字或使用,@JvmField这使它成为Java的静态最终常量。

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

5

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)

1
如何定义全局或静态方法?
Samad Talukder

@SamadTalukder在Kotlin会很有趣sumValue(v1:Int,v2:Int):Int {return v1 + v2}
Shomu

5

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

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

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

const val COMMUNITY_NAME = "wiki"

用Java编写的类似代码将是:

final static String COMMUNITY_NAME = "wiki";

除上述答案外-

@JvmField 用来指示Kotlin编译器不要为此属性生成getter / setter,并将其公开为字段。

 @JvmField
 val COMMUNITY_NAME: "Wiki"

静态场

在命名对象或伴随对象中声明的Kotlin属性在该命名对象或包含伴随对象的类中将具有静态后备字段。

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

  • @JvmField 注解;
  • lateinit 修饰符
  • const 修饰符。

此处有更多详细信息-https: //kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields


4

在任何答案中都没有提到的是使用的开销companion objects。如您在这里所读,伴随对象实际上是对象,创建它们会消耗资源。另外,每次使用常量时,您可能需要使用多个getter函数。如果您只需要一些基本常量,则可能会更好,val因为它可以获得更好的性能并避免使用companion object

TL; DR; 的文章:

使用随播对象实际上会变成这段代码

class MyClass {

    companion object {
        private val TAG = "TAG"
    }

    fun helloWorld() {
        println(TAG)
    }
}

变成这段代码:

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

因此,尽量避免它们。


3

局部常数:

const val NAME = "name"

全局常数:

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

访问MyConstants.NAME


1

您可以通过几种方法在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方法,您应该在val之前使用const,如下所示,

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

因此,这是创建常量的最佳方法。


0

对于基元和字符串:

/** 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!"
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.