android – Constants in Kotlin — what's a recommended way to create them?-ThrowExceptions

Exception or error:

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 …?

How to solve:

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"

}

###

Avoid using companion objects. Behind the hood, getter and setter instance methods are created for the fields to be accessible. 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 practice :

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

###

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

###

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()

###

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 or you can also put them inside any existing Kotlin file) 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

###

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

###

Kotlin static and constant value & method declare

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
}

}

Access value anywhere

val value = MyConstant.PI
val value = MyConstant.sumValue(10,5)

###

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

###

local constants:

const val NAME = "name"

Global constants:

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

access MyConstants.NAME

###

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.

###

There are a few ways you can define constants in Kotlin,

Using companion object

    companion object {
        const val ITEM1 = "item1"
        const val ITEM2 = "item2"
    }

you can use above companion object block inside any class and define all your fields inside this block itself. But there is a problem with this approach, the documentation says,

even though the members of companion objects look like static members in other languages, at runtime those are still instance members of real objects, and can, for example, implement interfaces.

When you create your constants using companion object, and see the decompiled bytecode, you’ll something like below,

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

From here you can easily see what the documentation said, even though the members of companion objects look like static members in other languages, at runtime those are still instance members of real objects It’s doing extra work than required.

Now comes another way, where we don’t need to use companion object like below,

object ApiConstants {
      val ITEM1: String = "item1"
 }

Again if you see the decompiled version of the byte code of above snippet, you’ll find something like this,

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

Now if you see the above decompiled code, it’s creating get method for each variable. This get method is not required at all.

To get rid of these get methods, you should use const before val like below,

object ApiConstants {
     const val ITEM1: String = "item1"
 }

Now if you see the decompiled code of above snippet, you’ll find it easier to read as it does the least background conversion for your code.

public final class ApiConstants {
    public static final String ITEM1 = "item1";
    public static final ApiConstants INSTANCE;

    private ApiConstants() {
     }

    static {
        ApiConstants var0 = new ApiConstants();
        INSTANCE = var0;
      }
    }

So this is the best way to create constants.

###

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

Leave a Reply

Your email address will not be published. Required fields are marked *