Constants in Kotlin — what's a recommended way

2019-03-11 13:32发布

问题:

How is it recommended to create constants in Kotlin? And what's the naming convention? I've not found that in the documentation.

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

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

Or ...?

回答1:

In Kotlin, if you want to create the local constants which are supposed to be used with in the class then you can create it like below

val MY_CONSTANT = "Constants"

And if you want to create a public constant in kotlin like public static final in java, you can create it as follow.

companion object{

     const val MY_CONSTANT = "Constants"

}


回答2:

Avoid using companion objects. Behind the hood getter and setter instance method is created for the fields to be accessible and calling instance methods is technically more expensive than calling static methods.

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

Instead define the constants in object.

Recommended practise :

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

and access them globally like this: DbConstants.TABLE_USER_ATTRIBUTE_EMPID



回答3:

Values known at compile time can (and in my opinion should) be marked as constant.

Naming conventions should follow Java ones and should be properly visible when used from Java code (it's somehow hard to achieve with companion objects, but anyway).

The proper constant declarations are:

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


回答4:

You don't need a class, an object or a companion object for declaring constants in Kotlin. You can just declare a file holding all the constants (for example Constants.kt) and directly declare the constants inside the file. The constants known at compile time must be marked with const.

So, in this case, it should be:

const val MY_CONST = "something"

and then you can import the constant using:

import package_name.MY_CONST

You can refer to this link



回答5:

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 you'll probably just be better off using val to get a better performance and avoid the companion object.

TL;DR; of the article:

Using companion object actually turns this code

class MyClass {

    companion object {
        private val TAG = "TAG"
    }

    fun helloWorld() {
        println(TAG)
    }
}

Into this code:

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

So try to avoid them.



回答6:

If you put your const val valName = valValue before the class name, this way it will creates a

public static final YourClass.Kt that will have the public static final values.

Kotlin:

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

Java decompiled:

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


回答7:

local constants:

const val NAME = "name"

Global constants:

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

access MyConstants.NAME



回答8:

Checkout this article. It gives a nice overview of different ways in which you can store your constants, with related performance trade-offs.



回答9:

class Myclass {

 companion object {
        const val MYCONSTANT = 479
}

you have two choices you can use const keyword or use the @JvmField which makes it a java's static final constant.

class Myclass {

     companion object {
           @JvmField val MYCONSTANT = 479
    }

If you use the @JvmField annotation then after it compiles the constant gets put in for you the way you would call it in java.
Just like you would call it in java the compiler will replace that for you when you call the companion constant in code.

However, if you use the const keyword then the value of the constant gets inlined. By inline i mean the actual value is used after it compiles.

so to summarize here is what the compiler will do for you :

//so for @JvmField:

Foo var1 = Constants.FOO;

//and for const:

Foo var1 = 479


回答10:

First of all, the naming convention in Kotlin for constants is the same than in java (e.g : MY_CONST_IN_UPPERCASE).

How should I create it ?

1. As a top level value (recommended)

You just have to put your const outside your class declaration.

Two possibilities : Declare your const in your class file (your const have a clear relation with your class)

private const val CONST_USED_BY_MY_CLASS = 1

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

Create a dedicated constants.kt file where to store those global const (Here you want to use your const widely across your project) :

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

Then you just have to import it where you need it :

import com.project.constants

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

2. Declare it in a companion object (or an object declaration)

This is much less cleaner because under the hood, when bytecode is generated, a useless object is created :

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

Even worse if you declare it as a val instead of a const (compiler will generate a useless object + a useless function) :

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

Note :

In kotlin, const can just hold primitive types. If you want to pass a function to it, you need add the @JvmField annotation. At compile time, it will be transform as a public static final variable. But it's slower than with a primitive type. Try to avoid it.

@JvmField val foo = Foo()


回答11:

For primitives and Strings:

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

For other cases:

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

Example:

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