Why does Kotlin need "const" keyword?


#1

It’s a compile error to specify const when the assignment expression isn’t a compile-time constant.
On the other hand, if the assignment expression is a compile-time constant, IDEA suggests to add const keyword.
Hence the question: why does the language need it at all, if the compiler can infer it just fine? What would change if we never use this keyword and let the compiler auto-infer it, so that it can be used for optimizations and whatever the other uses of const currently are.


#2

There are situations, where const would not be what you want. Mostly when you want to allow for java interop. Java does not now about the const value and therefor can not access it.
Another reason, why you might not want it, is if you are doing some stuff with reflection or custom code generation, where you need to access the value.


#3

If there are cases when you don’t want it, then it should be opt-out. Why then it is a keyword, and not an annotation, like @JvmField and such?

Also, I don’t understand the point about reflection and interop. As far as I can see, if you declare a const val in a file or an object, the corresponding generated JVM object will have a static final field. Could you expand on that a bit more?


#4

This answer is wrong, corrected in my below!

old answer

Your understanding of const is wrong. If you take a look at the created bytecode you see that a val will create a final field. It will be static if it is top level or annotated with @JvmStatic. A const val will be inlined by the constructor, but can not contain any function calls, therefor it creates nothing accessible by the JVM. The difference to inline val is that it will create a private backing field with a getter which will be inlined.

That is also the reason why it is a keyword and not an annotation. It is something completely different from a normal property. IMO the val keyword should be dropped if anything as this no longer is a property in the normal meaning of the word.


#5

Sorry, I can’t quite get this phrase.

Here’s the source:

val foo1 = 42
const val foo2 = 43

object Bar {
  val bar1 = "bar1"
  const val bar2 = "bar2"
}

fun main(args: Array<String>) {
  println(foo1)
  println(foo2)
  println(Bar.bar1)
  println(Bar.bar2)
}

And here’s what javap -p shows:

Compiled from "Foo.kt"
public final class FooKt {
  private static final int foo1;
  public static final int foo2;
  public static final int getFoo1();
  public static final void main(java.lang.String[]);
  static {};
}
Compiled from "Foo.kt"
public final class Bar {
  private static final java.lang.String bar1;
  public static final java.lang.String bar2;
  public static final Bar INSTANCE;
  public final java.lang.String getBar1();
  private Bar();
  static {};
}

As you can see, the difference is only whether the field will be private or public. I can’t see how that impedes interop nor reflection.


#6

Woops, your right. I was 100% sure that it would not create any fields. In that case this will only get inlined by the kotlin compiler and it can still be accessed by java. In that case I would agree, an annotation would maybe be better.
There is still one important difference between const val and val which I guess is the reason to make it a keyword.

val foo1 = 42
const val foo2 = 43
println(foo1) => println(foo)
println(foo2) => println(43)

This is still true, if you define foo2 in a different library. This means you can not use const inside of a library and change it later. Any kotlin program using the library would need to be recompiled to access it.

Oh, yeah and I tested it this time instead answering from memory :man_facepalming: :wink:


#7

Thanks, Burkhard! This is a valid reason indeed.


#8

However, some questions still remain.

First, what is the benefit of const? Doesn’t JIT already inline frequently used constants? Are there any reports on the measured improvement in performance with const?

Second, why does IDEA then suggest to add const? Looks like it should be used only when you’re sure it won’t be used in other modules. That is, why does it suggest adding const to a public const val?


#9

Consts are needed to access them statically. For example, if you want to use constant in annotation, it must be marked as const. I am not sure that the keyword is really needed, but for now, you can’t avoid it.


#10

Thanks, this actually illustrates my point! If you are using inside an annotation a val from the same compilation unit, then you should’t need const, since the compiler can check if val is a compile-time constant. If you’re using val from another compilation unit, then you probably shouldn’t use const because of the reasons pointed out by @Wasabi375: it breaks when the module you depend on is recompiled!


#11

This topic made me think for some reason about one interesting feature of const. It should be possible to build a library version check, ensuring its version is equal or higher than the version used to compile with.

Let’s say we have a library with the following code

class Version(val v: String): Comparable ...
private const val compiledVersion = Version("1.0")
val version = libraryVersion
inline fun checkVersion() = assert(version >= compiledVersion)

At first this seems to do nothing, but if called from a different application, this will ensure at runttime that the library is of the right version. Combined with static analysis and a annotation like SinceVersion this could be quite powerful for library authors. The only problem is that checkVersion fails if it is called without inlining it.


#12

That’s an interesting point. However, if you’re using something like Maven, you don’t need it.


#13

I was thinking more about a situation in which this could ensure version compatibility not during development, but on the end users computer or in case of kotlin2js third party libraries.
That being said, I know that there are already a lot of different and established ways of ensuring version compatibility on all of those different systems.
I just thought it would be an interesting use case displaying the way const could be used outside of annotations.


#14

Unfortunately, that won’t compile (at least on Kotlin-JVM, not sure if the rules are relaxed for Kotlin-JS and Kotlin-Native). Const values can only be “primitives” and Strings, i.e. things that can be constants in a Java class file.

Constants in Java (by which I mean static final fields initialized with compile time computable values since Java has no const identifier) are required to be inlined instead of accessing the actual field. It still generated the field but other classes that accessed it would have the value inlined and no reference to the field added to the class file. This can lead to some issues if you replaced the class file that defined the constant with a different value without recompiling the classes that accessed it they would still use the old value.


#15

Woops, I only tested it with a simple string, in which it would. Not sure if inline classes are allowed for constants. Might be an interesting feature.