Why do optionals in Kotlin require explicit initialisation? - kotlin

When defining an optional property in a class, Kotlin requires that it is explicitly initialised as null, like so:
var myString: String? = null
Is there any reason that the compiler cannot infer this initial value? I believe Swift would let you skip the = null part, however this is a compiler error in Kotlin. Wouldn't it be simpler to automatically have the value null after writing the following?
var myString: String?

Explicitness is a part of the overall language design in Kotlin. There are no implicit defaults for any types in Kotlin language. There is also desire to discourage (mis)use of nulls, so in respect to initialization nulls are not considered special in any way. If you have a non-nullable string var myString: String they you are required to initialize it with something just like you are required to initialize a nullable string var myString: String? with something, so this way its initial value is always explicit.
Note, technically speaking, String? in Kotlin is not an optional string in Kotlin. In Kotlin it is called a nullable string. However, the most common use-case for nulls is to represent the "absence of value".

There is no reason null must be the initial value of uninitialized variables.
It is not inference.
It is just a rule in Swift, and Kotlin does not have such rule.
Which do you think var a: Int? should be initialized as? 0 or null? Both arguments may have some reasons.
And in Kotlin, nullables are not optionals.

Related

How to stop Kotlin from widening types?

So I'm trying to define a method like this
fun <R,F> myFunction(prop: KProperty1<R, F>, value:F) {}
// so that the compiler only allows me to invoke it like
myFunction(User::name, "Alejandro")
// and stops developers from doing illegal things like
myFunction(User::name, 123)
//However, compiler doesn't complain if I do that... it widens the type to Any
How can I achieve that?
Kotlin is "widening" the type here because the value type parameter (i.e. the second type parameter) of KProperty1 is defined with keyword out which makes that parameter covariant.
This means that for instance KProperty1<User, String> is a subtype of KProperty1<User, Any>, and hence User::name which is presumably a KProperty1<User, String>, can also be seen as a special case of KProperty<User, Any>. Therefore, it is totally legal to call myFunction<User,Any>(User::name, 123).
The logic behind this can be derived from the name of the out keyword: It is expected that the typed parameter is only used in "out position" of any function call. In the case of KProperty1 this makes sense, because it is the type of the return value of the property. When you get a value from a KProperty1<K, V>, that value is of type V and thus it can be used anywhere where it is okay to have some supertype of V.
This should only be a problem, if you want to use the value in the "in position" of some function, for instance, if you want to write a function that takes a value of type V and store it in a KProperty1<K, V>.
If this is what you want, you are lucky, because you can and should just use KMutableProperty1<K,V> where the value parameter does not have an out keyword which means that it is invariant. Also, that interface allows you to put the value into the property.
Changing your function definition to
fun <R,F> myFunction(prop: KMutableProperty1<R, F>, value:F) {}
makes that the compiler allows myFunction(User::name, "Alejandro"), but it complains on myFunction(User::name, 123).
See also: Kotlin documentation on Variance

What is this Kotlin type: (String..String?)

IntelliJ is showing me context hints that my variables are of type (String..String?). I cannot find any mention of it on the internet, what is this type?
(String..String?) represents a flexible type with lower bound String and upperbound String? (nullable string). This is not valid Kotlin code (it's not denotable) but it is used in the compiler internals and thus in IntelliJ's hints sometimes.
(On the JVM we often see platform types using ! as in String!, which are a more specific case of flexible types)
It's Kotlin's way of saying it doesn't know whether the String type declared for payload.email is nullable or not (for instance if this is declared in Java, which doesn't distinguish those), and yet it doesn't want to enforce either of those, for convenience (hence "flexible").
As the name suggests, flexible types are flexible — a value of type (L..U) can be used in any context, where one of the possible types between L and U is needed
This means that even though the actual type of the value is "somewhere between String and String?", values of this type can be used even in places expecting String, even though the real type of that value may be String? and thus the value could be null.
This is useful because assuming it is String would mean that null checks would be marked as redundant, and assuming it is String? would force the developer to write null checks everywhere, even though they might know that this particular Java method cannot return null.
In general, it's a good practice to explicitly declare the type of a variable that you get from Java, to avoid the propagation of the platform type and the uncertainty (and unsafety) that comes with it:
val email: String = payload.email // if you know it cannot be null
val email: String? = payload.email // if you don't know

Why is my "List<String>" being interpreted as "List<String>?"

class Example(private val childrenByParent: HashMap<String, List<String>>) {
private val parents: List<String> = childrenByParent.keys.toList()
fun getChildrenCount(parentPosition: Int): Int {
return childrenByParent[parents[parentPosition]].size
// error, recommends using "?." or "!!"
}
}
The compiler won't let me call size directly but I don't understand why. There are no nullable types in sight.
If I let the compiler infer the type by doing this:
val infer = childrenByParent[parents[parentPosition]]
I can see that it assumes it's a List<String>?
It seems that I'm quite confused about nullability still. Would appreciate some help. I have a feeling I'm doing something incredibly dumb, but after some searching and testing I failed at fixing this.
I would like for this function to not use ?. or even worse, !!. Is it possible? At least, using HashMap and List<String>.
HashMap.get(Object) returns null when there is no element matching the key you provided, so its return type is effectively nullable, regardless of whether the values are or not.
So unfortunately you have to account for the case in which the key doesn't exist, so your choices are either implementing a case where it doesn't, or just declaring it as non-null with !! if you are sure the key exists.
Otherwise you can use HashMap.containsKey(String) to ensure the key exists and then you can be confident that using !! on the value won't result in a NullPointerException.
However as #gidds pointed out, this is not naturally thread-safe without some more work, so it might be best to just handle the case of the key not being in the map. Also I cannot actually think of many cases where you could be sure that key exists, in which a Map is the most appropriate data structure to use.
Also, even though this is not the case here, remember that nullability is just a feature of Kotlin, so when using some classes originally written in Java, whether an element is nullable or not is unknown. The IDE will usually represent this as Type! where the single ! tells you it is a platform type.

What is the scope of casted variable in Kotlin?

When casting a variable at the right side of the assign, i'm surprisely realize that the variable still behave as the casted type and not as it was original defined.
Am i doing something wrong or it's a compiler issue?
Code:
val hippoList = listOf<Hippo>(Hippo())
val hippoMutableList : MutableList<Hippo> = hippoList as MutableList<Hippo>
hippoList.add(Hippo())
since hippoList is from a List type, it is immutable. So how does trying to run add function on an immutable type isn't cause to compilation error?
If you're doing casting it means that you know more than a compiler about this context of execution and you're telling the compiler that this hippoList is a MutableList so on every next usage of hippoList compiler already knows that this have to be a MutableList and allows you to use add method because you casted it to MutableList previously. In fact you will get a runtime error UnsupportedOperationException which means that you didn't really know more about this context of execution and you did something wrong. So instead of using casting on your own allow compiler to do it's work.
In your case instead of a casting to MutableList, transform hippoList to MutableList with
hippoList.toMutableList()
The same happens when you're using !! from nullable type to not null type, when you're using it when you know more than a compiler about the context of execution. Here's a little example
val someNullableType: String? = null
val thisStringIsNotNull = someNullableType!!
by using !! on someNullableType we're telling the compiler that someNullableType is not null as well, so we're allowed to write (as in you're case where you're telling that your List is a MutableList as well)
someNullableType.length
but we will receive exception earlier (in place where we used !! to tweak the compiler)

What different StringBuilder and StringBuilder! in kotlin? [duplicate]

This question already has answers here:
Single exclamation mark in Kotlin
(7 answers)
Example of when should we use run, let, apply, also and with on Kotlin
(6 answers)
Closed 2 years ago.
In the code below. I found in Intellij Idea compiler that val a and val b by default are "val a: StringBuilder" & "val b: StringBuilder!"
what is the difference between the two? What's the difference between StringBuilder and StringBuilder! ? Thank you :)
fun main(){
val a = StringBuilder().apply { // by default is val a : StringBuilder
append("Hello ")
append("from me")
}
println(a)
val b = StringBuilder().run { // by default is val b : StringBuilder!
append("Hello ")
append("from me")
}
println(b)
}
The ! indicates a platform type. It means that the compiler can't tell whether the type is nullable or not, because it comes from Java (or another JVM language), which doesn't make the distinction between nullable and non-nullable types, and doesn't have an annotation (#Nullable or #NonNull) to indicate that.
As a result, the compiler won't be able to make its usual null checks, so you should take care.
If you know (from the documentation, or looking at the Java code, or whatever) whether the value could be null or not, it's a good idea to specify the type explicitly (as either nullable with a trailing ?, or non-nullable without).
In this case, the difference is that apply() returns the value it was called on; that's all Kotlin, so the compiler knows its type. However, run() returns the last value in the lambda, which is the result of the last append() call. That method is defined in Java (since StringBuilder is part of the Java standard library), so the compiler can't tell whether it's nullable or not. But it's clear from the documentation that the method simply returns the StringBuilder it was called on, and so cannot be null. So for safety, you could specify an explicit StringBuilder type (i.e. non-nullable) for b.