I am new to kotlin, and I have been doing research on the syntax of the language. It is to my understanding that in kotlin you can cast data types using integrated functions like :
.toInt()
converting 3.14 to an integer :
3.14.toInt()
since it is known that the readline() function returns a string i am not sure why this syntax is correct:
fun main() {
println("please enter a int:")
val num1 = readLine()!!.toInt()
println("one more")
val num2 = readLine()!!.toInt()
println("sum : ${num1 + num2}")
}
and this syntax is incorrect
fun main() {
println("please enter a int:")
val num1 = readLine().toInt()
println("one more")
val num2 = readLine().toInt()
println("sum : ${num1 + num2}")
}
returns the error:
Error:(5, 26) Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String
Just looking for a bit more of an explanation on casting and how the syntax differs when it comes to the readline() function and functions alike.
The method readLine() returns a String? - the question mark means it can either be null or a String. In Kotlin, you need to handle instances with nullable type with either ? or !! when you're invoking a method onto that instance.
The difference is that ? only proceeds when the instance is not null, and !! forces it to proceed. The latter may give you a NullPointerException.
For example:
val num1 = readLine()?.toInt()
// Here, num1 could either be a String or null
val num1 = readLine()!!.toInt()
// if it goes to this next line, num1 is not null. Otherwise throws NullPointerException
readLine() returns String? (nullable version of String?)
Function toInt() receives String (non-nullable type).
fun String.toInt(): Int // non-nullable
fun String?.toInt(): Int // nullable (call)
You must do some kind of a null check to be sure that toInt will called on a non-nullable object. The !! operator converts nullable String? type to non-nullable String.
Related
In the below code:
val sum = listOf(1, 2, 3).sumOf { if (it % 2 == 0) 1 else 0 }
Kotlin gives the following error:
Kotlin: Overload resolution ambiguity:
public inline fun <T> Iterable<TypeVariable(T)>.sumOf(selector: (TypeVariable(T)) -> Int): Int defined in kotlin.collections
public inline fun <T> Iterable<TypeVariable(T)>.sumOf(selector: (TypeVariable(T)) -> Long): Long defined in kotlin.collections
Playground
If I explicitly use toInt(), the error is gone but I get a warning of redundant call
val sum = listOf(1, 2, 3).sumOf { if (it % 2 == 0) 1.toInt() else 0 }
Why doesn't Kotlin automatically use Int here?
The spec says the following about the types of integer literals:
A literal without the mark has a special integer literal type
dependent on the value of the literal:
If the value is greater than maximum kotlin.Long value, it is an illegal integer literal and should be a compile-time error;
Otherwise, if the value is greater than maximum kotlin.Int value, it has type kotlin.Long;
Otherwise, it has an integer literal type containing all the built-in integer types guaranteed to be able to represent this value.
So integer literals like "1" doesn't have a simple type like kotlin.Int or kotlin.Long. It has an "integer literal type".
Example: integer literal 0x01 has value 1 and therefore has type ILT(kotlin.Byte,kotlin.Short,kotlin.Int,kotlin.Long). Integer literal 70000 has value 70000, which is not representable using types kotlin.Byte and kotlin.Short and therefore has type ILT(kotlin.Int,kotlin.Long).
Here are the subtyping rules of these ILTs. Importantly for your question:
∀Ti∈{T1,…,TK}:ILT(T1,…,TK)<:Ti
This rule basically says that ILTs work like an intersection type. For example, ILT(kotlin.Int,kotlin.Long) is a subtype of kotlin.Int and also a subtype of kotlin.Long.
Now let's look at your lambda { if (it % 2 == 0) 1 else 0 }. It returns either the literal 0 or the literal 1. These both have the type:
ILT(kotlin.Byte,kotlin.Short,kotlin.Int,kotlin.Long)
which is a subtype of kotlin.Long and kotlin.Int. Therefore, your lambda can be converted to both a (T) -> Long and a (T) -> Int, in the same way that a (T) -> Dog can be converted to a (T) -> Animal.
When you use toInt(), then only the (T) -> Int overload matches the return type, since Int is not convertible to Long implicitly.
Apparently, if you do toInt() on the whole expression, there is no redundant toInt warning:
fun main() {
val sum = listOf(1, 2, 3).sumOf { (if (it % 2 == 0) 1 else 0).toInt() }
}
Also note that the compiler looks at the lambda return type only because sumOf is annotated with OverloadResolutionByLambdaReturnType. If not for this, you would still get an ambiguity error even if you use toInt(). See Using lambda return type to refine function applicability for more info.
The reason why it is able to choose the Int overload in simple cases like:
fun foo(x: Int) {}
fun foo(x: Long) {}
fun main() { foo(43) }
is because of the "Choosing the most specific candidate" step in overload resolution. In this step, it handles built in numeric types differently, and considers Int the "most specific". However, this step happens just before "Using lambda return type to refine function applicability", and thinks that (T) -> Int and (T) -> Long are equally specific.
example:
fun main(){
var userInput: String?
//userInput = null
userInput = "asbdef"
var inputLength:Int? = userInput!!.length
println("Length of the string is :"+inputLength)
}
Output :
Length of the string is :6
fun main(){
var userInput: String?
userInput = null
//userInput = "asbdef"
var inputLength:Int? = userInput!!.length
println("Length of the string is :"+inputLength)
}
Output :
Unresolved reference: length
I want to know why it gives compile error?
If I just replace (!!) operator with (?) it compiles well but prints output as null.
PS: I'm newbie in Kotlin
The ?. operator short-circuits if the left side evaluates to null. So the result of nullVariable?.length is null and .length is never evaluated. Your first example is effectively doing:
println("Length of the string is :" + null)
The !! operator says "throw a NullPointerException if the left side is null. Otherwise, we know it's not null on the right side". You can see this if you change your second example a little:
val userInput: String? = null
val inputLength:Int = userInput!!.length // throws NullPointerException
However, I'm not sure why are you are getting Unresolved reference: length. It looks like that the compiler is doing some optimization when you assign null directly to userInput, so rather than compiling it to a String? which throws an NPE at runtime, it knows the value is only null (not String?) at compile time, and therefore can't have the length property because null is not a reference.
You can force it to give you to NullPointerException by adding a layer of abstraction via a function call:
fun main(){
var userInput: String? = "foo"
userInput = alwaysNull()
val inputLength:Int = userInput!!.length // NullPointerException
}
fun alwaysNull(): String? = null
I don't see anything n the Null Safety documentation about the difference in behaviour between initializing in one line / via a function call vs. assigning null directly to a var though, so what's happening under the hood to get Unresolved reference is just a guess.
I created a Delegate class in Kotlin, for 'cyclic' variables, such as hours:
when '25' is added to an hour with initial value '0', the result should be '1',
since there is no such thing as '25' hours.
The class I created works, but when I reassign such a delegated variable,
and then print it using 'print(...)',
I get an Error saying 'Smart cast is impossible, X is a mutable variable'
Am I doing something wrong ?
How could I fix the problem ?
I have tried to add an explicit cast, which helped,
but gives a Warning saying 'No cast needed', while it does not work without the cast.
this is the minimal code to reproduce the Error:
package <package name>
import kotlin.reflect.KProperty
fun main(){
val test = Test()
test.hour = 25
print(test.hour)
// this works: print(test.hour as Number) ("No cast needed")
}
// just a class which uses the delegate
class Test {
var hour by CyclicVariable(24)
}
// the delegate class
class CyclicVariable(val max: Number, var value: Number = 0){
operator fun getValue(reference: Any?, property: KProperty<*>): Number = value.toDouble()
operator fun setValue(reference: Any?, property: KProperty<*>, value: Number) {
val result = value.toDouble() % max.toDouble()
this.value = if (result >= 0) result else max.toDouble() + result
}
}
expected result: 1.0 on console
actual result:
Error:(12, 11) Kotlin: Smart cast to 'Int' is impossible, because 'test.hour' is a mutable property that could have been changed by this time
The problem occurs, because there is no method print with Number parameter.
You can just use test.hour.toString().
Kotlin tries to cast this variable to Int, as it sees, that you set an int to the variable: test.hour = 25. But it can't, because the variable is mutable.
What is the difference between
x as? String
and
x as String?
They both seem to produce a String? type. The Kotlin page doesn't answer it for me.
UPDATE:
To clarify, my question is:
What is the purpose of having an as? operator at all, since for any object x and for any type T, the expression x as? T can be (I think) rephrased as x as T? ?
The difference lies in when x is a different type:
val x: Int = 1
x as String? // Causes ClassCastException, cannot assign Int to String?
x as? String // Returns null, since x is not a String type.
as? is the safe type cast operator. This means if casting fails, it returns null instead of throwing an exception. The docs also state the returned type is a nullable type, even if you cast it as a non-null type. Which means:
fun <T> safeCast(t: T){
val res = t as? String //Type: String?
}
fun <T> unsafeCast(t: T){
val res = t as String? //Type: String?
}
fun test(){
safeCast(1234);//No exception, `res` is null
unsafeCast(null);//No exception, `res` is null
unsafeCast(1234);//throws a ClassCastException
}
The point of the safe cast operator is safe casting. In the above example, I used the original String example with integers as the type. unsafeCast on an Int of course throws an exception, because an Int is not a String. safeCast does not, but res ends up as null.
The main difference isn't the type, but how it handles the casting itself. variable as SomeClass? throws an exception on an incompatible type, where as variable as? SomeClass does not, and returns null instead.
I have a method that converts ByteArray? to base64 String? so that if argument was null output will be null as well. This is its implementation:
fun toBase64String(array: ByteArray?): String? = if(array == null) null else
Base64.getEncoder().encodeToString(array)
But when I pass in not nullable ByteArray method returns String? which is expected. Is there a way to make it generic so such use case will be possible:
val base64 = toBase64String(ByteArray(4))
where base64 will be of type String and not String? since argument was not nullable?
I just started to work with Kotlin and probably don't know language feature that can make this possible.
You can make two overloads, one for nullable ByteArray? and one for non-null ByteArray:
fun toBase64String(array: ByteArray): String =
Base64.getEncoder().encodeToString(array)
#JvmName("toBase64StringNullable")
fun toBase64String(array: ByteArray?): String? =
if (array == null) null else toBase64String(array)
We need #JvmName("...") to avoid the declaration clash in the bytecode.
Also, this allows to distinguish the functions in Java.
Usage:
val nonNullBytes: ByteArray = TODO()
val nonNullString = toBase64String(nonNullBytes) // the inferred type is String
val nullableBytes: ByteArray? = TODO()
val nullableString = toBase64String(nullableBytes) // the inferred type is String?
When the argument is of the non-null type ByteArray, the compiler will choose the overload that returns a non-null String.
Probably overloading methods is the best solution for your case, but for the sake of completeness here are two other ways to realise that using only one method (the nullable one):
Not-Null-Asserted operator:
val base64: String = toBase64String(ByteArray(4))!!
Evlis operator:
val base64: String = toBase64String(ByteArray(4)) ?: "defaultString"
if argument was null output will be null as well
If that is the only thing the function does when it encounters null argument, it's better to declare it accepting non-null values and use safe call to deal with nulls:
fun toBase64String(array: ByteArray): String =
Base64.getEncoder().encodeToString(array)
val bytes: ByteArray? = ...
val base64 = bytes?.let { toBase64String(it) }
// the same can be written with function reference instead of lambda
val base64 = bytes?.let(::toBase64String)
Here let function is called only when bytes is not null, otherwise the result of the expression is null. When called it invokes the lambda function or the function reference specified as its argument, passing ByteArray which is already checked to be non-null to that function.
Also it can be more convenient to declare toBase64String as an extension for ByteArray, so it can be invoked with safe call without the helper function let"
fun ByteArray.toBase64String(): String =
Base64.getEncoder().encodeToString(this)
val bytes: ByteArray? = ...
val base64 = bytes?.toBase64String()