Does Kotlin have primitive types?

Does Kotlin have primitive types?. And I feel confused with two questions:
1/ When I declare the variable: val myAge: Int = 18 then the myAge variable stores the actual values is 18 or stores the addresses of the objects in the memory?.
2/ If Int is primitive type when I declare val myAge: Int = 18 then why we can use its method like myAge.toString() ?

No, it does not.

Please refer to official documentation: https://kotlinlang.org/docs/reference/comparison-to-java.html#what-java-has-that-kotlin-does-not

1 Like

Kotlin does have an unified type system where JVM primitives are used only as implementation detail. You shouldn’t bother with primitives as long as you don’t implement some kind of performance critical algorithms.

3 Likes

Also remember that JVM is only one of several Target platforms for Kotlin. On some of them the term primitive type is unknown (which does not mean there cannot be similar optimizations under the hood on those platforms).

So, Kotlin the language does not know primitive types. But the platform might have them or similar optimizations.

3 Likes

Primitives are a silly construct, which Kotlin does not attempt to bring in from Java. Everything in Kotlin is a class and Int is no exception, leading to a completely unified type system where everything inherits from Any. On Kotlin/JVM, when it is possible, the types that correspond to the Java primitives are compiled using the primitives, and when it is not, the class is used instead. In other words, you should not worry about primitives on the code side (because they don’t exist) and you should not worry about primitives on the implementation side (because they are handled automatically).

4 Likes

Two points.

First, I disagree that primitives are a silly construct. While I prefer Kotlin’s approach, the implementation was not as well understood back when Java was first developed, and primitives were a natural way to provide optimized run-time performance. See my JavaWorld article A case for keeping primitives in Java for details.

Second, even Kotlin provides some support for primitives in that one can use DoubleArray instead of Array<Double> when run-time performance is critical. Does it make a difference? Again, see section 2 “Single type system (almost)” of my second JavaWorld article Why Kotlin? Eight features that could convince Java developers to switch for details.

2 Likes

The only valid reason for primitives is performance (and the implementation detail that arithmetic operators only work on primitives - there are no intrinsics for the wrappers).

At the same time the primitives/objects distinction creates an inconsistency. They create a type systems where some times exist both as (immutable) reference types and value types and others as reference type only. Java addresses this by giving the boxed and unboxed versions of primitives different names. Kotlin does this by having the compiler “figure it out” and use primitives where possible.

The basis for the problem is the fact that on the JVM all objects are managed by reference (note that it may optimize this away when jitting), but that references are completely invisible. In many ways references have many advantages especially with polymorphism (because while the object sizes change, the reference size is independent of the type), but performance is not one of them. Identity semantics also seem to confuse people (two variables actually referring to the exact same object/aliassing). If you want full value semantics you end up needing a lot more compile time variance, the like of templates that C++ uses. This because for value types you need to adjust the code for the concrete type, and generic code has this issue.

On a virtual machine such as the JVM the bytecode is however sufficiently expressive that the JVM can do a lot of this code adjustment on demand. In that sense Project Valhalla inline types (was value types) seem to be interesting but nowhere near release. Inline/value types also create a consistent experience taking away the main drawback of primitives.

First, I disagree that primitives are a silly construct

They are a silly construct, why?.
Because a struct and a class with the same content should encode the same value, making them different is not meaningful because a value should be location and implementation dependent.
Deciding a type to be a value type or a reference type at the definition side is bad idea, it should be more a property of the binding or if not possible a property of a type on the call site.

For example:

class A
{
...
}

fun f(a:A){...} /*or*/  fun f(a:ref A)//a is a reference type
fun f(a:copy A){...} //a is a value type supporting a is compared with ==
fun f<T>(t:T){...} //T could be a value -or reference type
fun<copy A>(a) //T is a value type
fun<A>(a) //T is a reference type

copy states that mutations cannot be in-place, i.e. objects have to be copied and then mutated in place and that for each mutation.
Furthermore a instanceof copy A is equivalent to a instanceof A, because copy is an attribute of the type concerning only the variables of that type but not the semantic of the type itself.

Note: Value types (including primitive types) don’t say anything about boxing/unboxing, likewise where a value might be represented (heap,stack,register).

Kotlin actually does have a notion of primitive types, though I haven’t managed to find a clear definition or list of exactly which types are considered “primitive”. But it’s apparent in the language itself: lateinit var properties cannot be of a “primitive” type, whatever that is.

1 Like

Kotlin does not have primitive types. Primitives exist on the implementation level in JVM, so they are sometimes mentioned this way. They do not exist in the language itself as a separate concept and implementation differs on JS and Native.

But @tom000 have a good point. Documentation sometimes refers to primitives types, but it doesn’t explain them, because they don’t exist in the language. lateinit var is probably one of the best examples. Although, I must say I don’t understand why lateinit is not allowed on primitives instead of just boxing them. This is how primitives basically work in Kotlin: we use unboxed values if possible, we box them if needed.

1 Like

I agree. In my opinion, Kotlin documentation should be revised and cleared of some “Javisms” that complicate understanding. Lateinint was introduced for interoperability with some JVM libraries so it is tied to understanding JVM mechanics. I think it is a good idea to limit this feature to JVM target only in the future.

Here is the issue: https://youtrack.jetbrains.com/issue/KT-53334/Limit-lateinit-to-JVM-target-only

Hmm, honestly, I never considered lateinit to be mostly for Java interop. I don’t use it often, but it seems a good choice if we use some kind of initialization that is not based on constructors. It could be dependency injection framework or our custom initialization.

Delegates solve the same problem, but they add extra components for the whole life of the object (potential overhead, both CPU and memory). And for most of its life we don’t really need these delegates as the property is just non-null.

3 Likes

What we really need, imo, is something like the inline property delegates mentioned in the inline classes keep

  • Inline property delegates
class A {
    var something by InlinedDelegate(Foo()) // no actual instantiation of `InlinedDelegate`
}
@JvmInline
value class InlinedDelegate<T>(var node: T) {
    operator fun setValue(thisRef: A, property: KProperty<*>, value: T) {
        if (node !== value) {
            thisRef.notify(node, value)
        }
        node = value
    }
    operator fun getValue(thisRef: A, property: KProperty<*>): T {
        return node
    }

However, it seems as though that idea has been abnadoned for a while. It should be possible though with the copy funs that value classes KEEP mentions as a design idea:

private object Uninitialized
@JvmInline value class Lateinit<T> private constructor (private copy var underlying: Any?) {
    val isInitialized: Boolean get() = underlying != Uninitialized
}
operator fun Lateinit<T>.getValue(thisRef: Any?, property: KProperty<*>): T = 
    if(isInitialized) underlying as T else throw error("Uninitialized Property")
operator copy fun Lateinit<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T) { underlying = value }
fun <T> lateInit(): Lateinit<T> = Lateinit(Uninitialized)
// Usage
var myProperty by lateInit<Int>()

In the value classes KEEP, the section Copying Operators mentions:

Delegate convention (by) will need to be likewise tweaked to support copying setters (“withers”).

So there potentially is hope for the future! I think with those features, lateinit will become obsolete, save for the case of supporting DI frameworks