建议如何在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;
      }
    }

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

其他回答

避免使用伴随对象。在底层,为可访问的字段创建了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

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

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

const val MY_CONST = "something"

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

import package_name.MY_CONST

你可以参考这个链接

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

对于基元和字符串:

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