Kotlin中的常数 - 建议创建它们的方法是什么?

Jod*_*oro 112 android constants kotlin

如何在Kotlin中创建常量?什么是命名惯例?我没有在文档中找到它.

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

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}
Run Code Online (Sandbox Code Playgroud)

要么 ...?

AaR*_*RiF 98

在Kotlin中,如果你想创建应该在类中使用的本地常量,那么你可以创建它,如下所示

val MY_CONSTANT = "Constants"
Run Code Online (Sandbox Code Playgroud)

如果你想在kotlin中创建一个公共常量,比如java中的public static final,你可以按照以下方式创建它.

companion object{

     const val MY_CONSTANT = "Constants"

}
Run Code Online (Sandbox Code Playgroud)

  • 实际上,在类字段中命名“MY_CONSTANT”并不是一个好主意 - 编译器会发出警告,并显示消息**“私有属性名称‘MY_CONSTANT’不应在中间或末尾包含下划线”**。它打破了推荐的命名对流。 (5认同)
  • 你不需要`companion object` 我认为@piotrpo 答案应该是公认的 (3认同)
  • 如何在单独的文件(如名为“ Constants.kt”的新文件)中使用它,或者如何使用? (2认同)
  • 我使用一个文件作为常量。把我所有的常数都放在那里。 (2认同)
  • @Chiara 伴随对象(及其封闭类)充当命名空间,而不是顶级声明。我认为这两个答案都可以根据具体情况而有意义。 (2认同)

sud*_*esh 74

避免使用伴侣对象.在引擎盖后面,getter和setter实例方法是为可访问的字段创建的,并且调用实例方法在技术上比调用静态方法更昂贵.

public class DbConstants {
    companion object {
        val TABLE_USER_ATTRIBUTE_EMPID = "_id"
        val TABLE_USER_ATTRIBUTE_DATA = "data"
    }
Run Code Online (Sandbox Code Playgroud)

而是定义常量object.

推荐做法:

object DbConstants {
        const val TABLE_USER_ATTRIBUTE_EMPID = "_id"
        const val TABLE_USER_ATTRIBUTE_DATA = "data"
}
Run Code Online (Sandbox Code Playgroud)

并像这样访问全局: DbConstants.TABLE_USER_ATTRIBUTE_EMPID

  • 对于那些对本文感兴趣但无法访问上面 @dominik 的链接的人。这是工作链接:https://www.egorand.dev/where-should-i-keep-my-constants-in-kotlin/ (5认同)
  • @ErwinBolwidt我认为@sudesh的观点是,当结构的唯一目的是为某些常量值提供命名空间时,不应该使用类包装伴随对象设计。但是,如果您的结构需要可实例化,并且还包含几个“const val”,则声明“伴生对象”是正确的。 (4认同)
  • @ErwinBolwidt:sudesh是正确的,为同伴对象生成的字节码涉及在幕后使用吸气剂进行额外的对象创建。有关反编译Kotlin示例的详细说明,请参见https://blog.egorand.me/where-do-i-put-my-constants-in-kotlin/ (2认同)
  • 感谢@dominik,这是一篇非常详细的文章,我向所有想深入了解这一点的人推荐这篇文章,在很多情况下,kotlin 会产生次优字节码,jetbrains 已经解决了许多此类与性能相关的错误……请密切关注 https ://discuss.kotlinlang.org/ ,您将了解许多此类潜在方面。 (2认同)
  • 伴生对象也是一个对象。不知道为什么这会受到如此高度的支持。从技术上讲,是的,如果你想让它成为单例,你可以将类变成对象。但这可能会传播“根本不使用伴生对象”的想法,这是错误的。您也可以在伴随对象中使用“const val”。 (2认同)

pio*_*rpo 19

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

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

适当的常量声明是:

const val MY_CONST = "something"
const val MY_INT = 1
Run Code Online (Sandbox Code Playgroud)

  • 在那个链接中,我发布了他们说"如果有疑问,默认为Java编码约定" (11认同)
  • 在文档中指定@Jodimoro http://kotlinlang.org/docs/reference/coding-conventions.html (4认同)
  • `命名约定应遵循Java约定 - 为什么? (2认同)
  • Kotlin通常默认情况下遵循Java约定,如果没有另外指定,则使Interop平滑。 (2认同)
  • @Neil,不是。 (2认同)

Abd*_*ood 15

您不需要用于在Kotlin中声明常量的类,对象或伴随对象.您可以声明一个包含所有常量的文件(例如Constants.kt)并直接声明文件中的常量.编译时已知的常量必须标记为const.

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

const val MY_CONST = "something"

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

import package_name.MY_CONST

你可以参考这个链接

  • 常量必须在与它们相关的类中.如果你制作一个'常数'类,你最终将会结束数百个常数.Pe:MAX_WIDTH,MAX_HEIGHT必须在Screen类中,因此您可以在逻辑上访问它:Screen.MAX_WIDTH并且您不需要将Constants.SCREEN_MAX_WIDTH放在2年内与Constants.SCR_MAX_W和Constants.MAX_WIDTH重复,因为NOBODY当他们按下Ctrl +空格以自动完成时,滚动数百/ thousans线.说真的:不要这样做.导致不可维护性 (10认同)
  • 如果常量是真正的全局或具有大范围...例如所有包中使用的注释的值,或者由多个控制器等提取的标题名称,那么创建"常量"是完全可以接受的"适当的范围".但是,仅在特定上下文中使用的常量应限定为该上下文,并在相关类中声明. (4认同)
  • @ Herrbert74 对不起,我不同意你的看法。我同意有时很难找到哪个是,但一个固定的地方应该总是与它更相关的类。如果您希望稍后检索它们,将它们随机保存在随机数文件中并不是最好的方法......您会争辩说它们不会被随机存储,而是在与常量相关的包中,但这只是一个借口不要将它们放在与它们相关的类中,这最终是它们的位置...... (2认同)

A.M*_*ode 12

首先, 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 
}
Run Code Online (Sandbox Code Playgroud)

创建一个专用的constants.kt文件,在其中存储这些全局const(在这里您想在整个项目中广泛使用const):

package com.project.constants
const val URL_PATH = "https:/"
Run Code Online (Sandbox Code Playgroud)

然后,您只需将其导入所需的位置即可:

import com.project.constants

MyClass {
    private fun foo() {
        val url = URL_PATH
        System.out.print(url) // https://
    }
}
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

MyClass {
    companion object {
        val URL_PATH = "https://"
    }
}
Run Code Online (Sandbox Code Playgroud)

注意 :

在kotlin中,const只能保存基本类型。如果要向其传递函数,则需要添加@JvmField批注。在编译时,它将被转换为公共静态最终变量。但这比原始类型要慢。尽量避免它。

@JvmField val foo = Foo()
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案。无论如何,在这样的情况下: public static final Pattern REGEX_NOTEMPTY = Pattern.compile(".+") ???? (2认同)

Tha*_*ujo 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)
Run Code Online (Sandbox Code Playgroud)

Java反编译:

public final class MyClassKt {
    public static final int MY_CONST0 = 0;
    public static final int MY_CONST1 = 1;
}
// rest of MyClass.java
Run Code Online (Sandbox Code Playgroud)


Ano*_*p M 8

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

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

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

const val COMMUNITY_NAME = "wiki"
Run Code Online (Sandbox Code Playgroud)

用 Java 编写的类似代码是:

final static String COMMUNITY_NAME = "wiki";
Run Code Online (Sandbox Code Playgroud)

添加到上面的答案 -

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

 @JvmField
 val COMMUNITY_NAME = "Wiki"
Run Code Online (Sandbox Code Playgroud)

静态字段

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

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

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

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


Abh*_*mar 7

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

使用伴生对象

    companion object {
        const val ITEM1 = "item1"
        const val ITEM2 = "item2"
    }
Run Code Online (Sandbox Code Playgroud)

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

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

当您使用伴随对象创建常量并查看反编译的字节码时,您会看到如下所示的内容,

    companion object {
        const val ITEM1 = "item1"
        const val ITEM2 = "item2"
    }
Run Code Online (Sandbox Code Playgroud)

从这里你可以很容易地看到文档所说的内容,即使伴生对象的成员看起来像其他语言中的静态成员,但在运行时它们仍然是真实对象的实例成员,它做了超出要求的额外工作。

现在有另一种方式,我们不需要像下面这样使用伴生对象,

object ApiConstants {
      val ITEM1: String = "item1"
 }
Run Code Online (Sandbox Code Playgroud)

同样,如果您看到上面片段的字节码的反编译版本,您会发现类似的内容,

  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;
     }
  }
Run Code Online (Sandbox Code Playgroud)

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

要摆脱这些 get 方法,您应该在val之前使用const ,如下所示,

object ApiConstants {
     const val ITEM1: String = "item1"
 }
Run Code Online (Sandbox Code Playgroud)

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

object ApiConstants {
      val ITEM1: String = "item1"
 }
Run Code Online (Sandbox Code Playgroud)

所以这是创建常量的最佳方式。


j2e*_*nue 6

class Myclass {

 companion object {
        const val MYCONSTANT = 479
}
Run Code Online (Sandbox Code Playgroud)

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

class Myclass {

     companion object {
           @JvmField val MYCONSTANT = 479
    }
Run Code Online (Sandbox Code Playgroud)

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

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

所以在这里总结一下编译器将为您做的事情:

//so for @JvmField:

Foo var1 = Constants.FOO;

//and for const:

Foo var1 = 479
Run Code Online (Sandbox Code Playgroud)


Sir*_*lot 6

任何答案中都没有提到的是使用的开销companion objects。正如您可以在此处阅读的那样,伴生对象实际上是对象,并且创建它们会消耗资源。此外,每次使用常量时,您可能都需要执行多个 getter 函数。如果您需要的只是类的几个实例上的一些原始常量,那么您可能最好使用它val来获得更好的性能并避免companion object. 如果您的类有很多实例,那么代价就是更高的内存消耗,因此每个人都应该做出自己的决定。

TL;博士; 文章的:

使用伴生对象实际上将这个 Kotlin 代码变成:

class MyClass {

    companion object {
        private val TAG = "TAG"
    }

    fun helloWorld() {
        println(TAG)
    }
}
Run Code Online (Sandbox Code Playgroud)

进入这段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));
    }
}
Run Code Online (Sandbox Code Playgroud)


Sho*_*omu 6

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
}
Run Code Online (Sandbox Code Playgroud)

}

随时随地访问价值

val value = MyConstant.PI
val value = MyConstant.sumValue(10,5)
Run Code Online (Sandbox Code Playgroud)


Amj*_*aig 5

局部常量:

const val NAME = "name"
Run Code Online (Sandbox Code Playgroud)

全局常量:

object MyConstants{
    val NAME = "name"
    val ID = "_id"
    var EMAIL = "email"
}
Run Code Online (Sandbox Code Playgroud)

访问 MyConstants.NAME