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

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

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

或者…?


当前回答

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

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

其他回答

当地常量:

const val NAME = "name"

全局常量:

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

访问MyConstants。的名字

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

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

正确的常量声明是:

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

Something that isn't mentioned in any of the answers is the overhead of using companion objects. As you can read here, companion objects are in fact objects and creating them consumes resources. In addition, you may need to go through more than one getter function every time you use your constant. If all that you need is a few primitive constants on a few instances of your class, you'll probably just be better off using val to get a better performance and avoid the companion object. The trade off is higher memory consumption if you have many instances of your class so everyone should make their own decision.

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

首先,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()

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