Please help me understand this piece of code in the kotlin docs:-
val a: Int = 10000
print(a === a) // Prints 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA === anotherBoxedA) // !!!Prints 'false'!!!
Now, I understand that first int a = 10000 then in the next line it is comparing it using ===.
Now the question is why when it assigned boxedA=a, it checked if it is null using int?. Can it just be written like this:-
val boxedA: Int=a
Please if I'm understanding it the wrong way, someone guide to check the right place or explain it a bit for me.
First, the Int will be mapped to java int/Integer depending on its context. If Int is a generic argument, then its mapped type is Integer. Otherwise, it is a primitive type int. for example:
val a:Int = 1
//final int a = 1; //mapped to java code
val numbers:List<Int> = asList(1,2,3)
//final List<Integer> numbers = asList(1, 2, 3); //mapped to java code
Secondly, boxing an Int to an Int? is same behavior as java boxing an int to an Integer, for example:
val a:Int = 1 // int a = 1;
val b:Int? = a; // Integer b = a;
Why the boxed Integers are not the same?
This is because Integer only caching values in the range [-128, 127]. the the variable a above is out of the cache range, it will create a new Integer instance for each boxing rather than using a cached value. for example:
// v--- 1 is in cache range
val ranged: Int = 1
val boxedRanged1: Int? = ranged
val boxedRanged2: Int? = ranged
println(boxedRanged1 === boxedRanged2) //true
// v--- 128 is out of cache range
val excluded: Int = 128
val boxedExcluded1: Int? = excluded
val boxedExcluded2: Int? = excluded
println(boxedExcluded1 === boxedExcluded2) //false
when it assigned boxedA = a, it checked if it is null using int?
I have no idea what you mean by this. Making a variable have the type Int? makes it a variable that can either store an Int or null. There is no checking happening at this assignment. If you have a non-null value to assign to the variable, just make it non-nullable, without the ? in the type:
val copyOfA: Int = a
You can even omit the type, and get Int inferred:
val copyOfA = a
As for the comparisons:
== is used for comparing by value in Kotlin (this is the equivalent of using equals in Java), === is used for comparing references (this is == in Java).
When you create boxedA and anotherBoxedA, you create two Integer instances under the hood (because nullable variables can't be represented by primitives). These will be equal when compared with == (they have the same value), but not when compared with === (they are different instances).
You can see the relevant part of the offical docs here.
it checked if it is null using int?
That is not what it means.
Kotlin's null safety feature does not allow a variable to be set as null by default.
Check here.
val anotherBoxedA: Int? = a
This means that anotherBoxedA can be assigned null or is nullable.
val anotherBoxedA: Int? = null
This will be allowed.
Related
I expected that the type of a variable is promoted to a non-null type after a not-null check (like in the Dart language).
val someMap = mapOf("a" to 0L)
val a = someMap['a'] // a is of type Long?
if (a != null) {
val b = a // b is of type Long? and not of type Long. Why?
}
Can someone explain why this is not the case? Just a matter of taste of the language designers?
Since there is smart-casting, it doesn't matter. It will allow you to use members of a or b inside the if statement without null-safe calls (?.) or null assertions (!!). You can also safely declare b to be a Long without the compiler complaining:
if (a != null) {
val b: Long = a
}
It is I think a design choice for how implicit types should be inferred that b's type must be explicitly declared if you want it to be considered non-nullable. This is only relevant if passing it to a function with generics, since there is smart-casting.
What you can do instead of explicit null check is using let{} as follows:
val someMap = mapOf('a' to 0L)
val a = someMap['a'] // a is of type Long?
a?.let {
val b = it // b is of type Long
}
It is called smart casting, basically Kotlin is smart enough to determine that variable can no longer be null after check. More detail and can be found here if you are interested
As to why, only the creators of kotlin can know. But what you can do is this if you want a Long instead of Long? there is this
val b = a!!
I'm trying my hands on Kotlin. Being from a Python background is really giving me a tough time to get the knack of the Kotlin syntax. I'm trying to do a simple dictionary (Mutable Map) operation. However, its giving me exceptions.
This is what I tried. Kotlin compiler
Adding the code snippet for reference.
fun main() {
val openActivityMap = mutableMapOf<String, MutableMap<String, Any>>()
val packageName = "amazon"
val currentTime = 23454321234
if(openActivityMap.containsKey(packageName)){
if(openActivityMap[packageName]?.get("isAlreadyApplied")){
if((openActivityMap[packageName]?.get("lastAppliedAt") - currentTime) > 3600){
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
else{
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
}
I'm a bit late to the party, but I'd like to point out another solution here.
As I commented on the OP, heterogeneous maps with fixed string keys like this are usually better expressed with classes in Kotlin. For instance, in your case, the class for your main map's values could be the following:
data class PackageInfo(
var isAlreadyApplied: Boolean,
var lastAppliedAt: Long,
)
(you could obviously add more properties if need be)
This would save you all the casts on the final values.
Another point I'd like to make is that if you access the value for a key anyway, you don't need to check up front the existence of the key with containsKey. Maps return null for keys that are not associated with any value (this is why you need to check for null after getting the value).
The compiler cannot see the correlation between containsKey and the subsequent get or [] access. However, it's smart enough to understand a null check if you simply get the value first and then check for null.
This always applies unless you want to tell the difference between keys that aren't in the map and keys that are in the map but associated null values (which is quite rare).
All in all, I would write something like that:
fun main() {
val openActivityMap = mutableMapOf<String, PackageInfo>()
val packageName = "amazon"
val currentTime = 23454321234
val packageInfo = openActivityMap[packageName]
if (packageInfo != null) { // the key was found and the value is smart cast to non-null in the next block
if (packageInfo.isAlreadyApplied) {
if ((packageInfo.lastAppliedAt - currentTime) > 3600) {
packageInfo.isAlreadyApplied = false
}
} else {
packageInfo.isAlreadyApplied = false
}
}
}
data class PackageInfo(
var isAlreadyApplied: Boolean,
var lastAppliedAt: Long,
)
I would recommend writing tests first and working in small increments, but this should fix your compilation issues:
fun main() {
val openActivityMap = mutableMapOf<String, MutableMap<String, Any>>()
val packageName = "amazon"
val currentTime = 23454321234
if(openActivityMap.containsKey(packageName)){
if(openActivityMap[packageName]?.get("isAlreadyApplied") as Boolean){
if((openActivityMap[packageName]?.get("lastAppliedAt") as Long - currentTime) > 3600){
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
else {
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
}
EDIT: Also I prefer to avoid nullable variables and mutable objects in general, but I suppose there's an exception to every rule.
Couldn't you just declare your Map<String, Any> to return a Boolean instead of Any? So,
val openActivityMap = mutableMapOf<String, MutableMap<String, Boolean>>()
It looks like you're trying to use your second Map to store both Booleans and Ints, which is complicating the logic. You'll need to typecast if you decide to approach it without Typing.
There's a problem with the 2 statement below
if(openActivityMap[packageName]?.get("isAlreadyApplied"))
if((openActivityMap[packageName]?.get("lastAppliedAt") - currentTime) > 3600)
As we all know, an IF statement requires a boolean value for it's param. The types of both statement are unknown at compilation time as they are of a Generic type, Any. As such,
openActivityMap[packageName]?.get("isAlreadyApplied") could be a null or of type Any (Not Boolean).
openActivityMap[packageName]?.get("lastAppliedAt") could be a null or of type Any (an Int was expected here for computation).
This would throw compilation errors as the compiler does not know the types to go with. What could be done is to cast to it's proper types.
Solution
openActivityMap[packageName]?.get("isAlreadyApplied") as Boolean ?: false
((openActivityMap[packageName]?.get("lastAppliedAt") as Int ?: 0) - currentTime)
Giving a default value if it's null.
maybe you can try something like this
if (openActivityMap.containsKey(packageName)) {
val packageMap = openActivityMap[packageName]!!
val applyRequired = (packageMap["lastAppliedAt"] as Long - currentTime) > 3600
packageMap["isAlreadyApplied"] = packageMap.containsKey("isAlreadyApplied") && !applyRequired
}
btw. do you really want to have lastAppliedAt to be in te future? otherewise it will never be > 3600
This question already has answers here:
Integers caching in Java [duplicate]
(2 answers)
Closed 2 years ago.
I do not understand this snippet of code I found on the official documentation:
fun main() {
val a: Int = 100
val boxedA: Int? = a
val anotherBoxedA: Int? = a
val b: Int = 100000
val boxedB: Int? = b
val anotherBoxedB: Int? = b
println(boxedA === anotherBoxedA) // true
println(boxedB === anotherBoxedB) // false
}
Why the equality changes when the variable's value change?
The JVM caches Integers for values between -128 and 127. Integers outside that range may or may not be cached - if they aren't, then each call to Integer i = 128 will return a new object.
Kotlin inherits this behaviour (an Int? in kotlin is an Integer in Java).
So, back to your example:
100 gets cached, hence boxedA and anotherBoxedA are the same object
but 100000 is not cached and 2 different instances of Integers are returned
This question already has an answer here:
kotlin int boxed identity
(1 answer)
Closed 4 years ago.
I am a beginner of kotlin.
I do not understand the output below.
#Test
fun testNumberBoxing() {
val a:Int = 1000
val boxedA1: Int? = a
val boxedA2: Int? = a
println("first check = ${boxedA1 === boxedA2}")
val b: Int = 2
val boxedB1: Int? = b
val boxedB2: Int? = b
println("second check = ${boxedB1 === boxedB2}")
}
result is
first check = true
second check = false
Why are the two outputs different?
my kotlin version is 1.2.31
org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.31
The output I got
first check = false
second check = true
What the code compiles to
public static final void main(#NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
int a = 1000;
Integer boxedA1 = Integer.valueOf(a);
Integer boxedA2 = Integer.valueOf(a);
String var4 = "first check = " + (boxedA1 == boxedA2);
System.out.println(var4);
int b = 2;
Integer boxedB1 = Integer.valueOf(b);
Integer boxedB2 = Integer.valueOf(b);
String var7 = "second check = " + (boxedB1 == boxedB2);
System.out.println(var7);
}
Why valueOf is not consistent
For this we need to look at the JavaDoc of valueOf:
Returns an Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values. This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.
As you can see, for small values it will return the same object on both calls, so they are equal, and for the larger uncached value the 2 objects are different
In java the == checks for object equality, so the 2 equal objects are false, while 2 copies of the same object returns true.
That's rather weird, I'm consistently (both locally and on try.kotlinlang.org with different Kotlin versions) getting this result instead:
first check = false
second check = true
And this is what is to be expected, as the JVM caches Integer instances int the -127 to 128 range, reusing the same Integer instance when one is required for boxing, literals, or Integer.valueOf calls in this range.
In contrast to php or javascript where === is in most cases the more sane option, in Kotlin === compares references to objects instead of values.
As others pointed out, objects for Integers of the same value can be cached. Same goes for Strings and the other primitive types.
I'm giving myself until 12:00AM to learn and get productive (hopefully) on kotlin.
Following https://kotlinlang.org/docs/kotlin-docs.pdf I tried these snippets on page 17. Could anyone please help me understand why === returns true if a value is between -128 to 127?
The following indeed prints false:
val a: Int = 10000
val boxedA: Int? = a // Integer#445
val anotherBoxedA: Int? = a // Integer#447 why?
print(boxedA === anotherBoxedA) // false
However changing a to any value between -128 to 127 always prints true:
val a: Int = -128
val boxedA: Int? = a // Integer#445
val anotherBoxedA: Int? = a // Integer#445 why?
print(boxedA === anotherBoxedA) // true!
It seems to me if Int value is outside the bounds of -128 to 127 (Java byte) kotlin creates a new object on assignment does making the reference not equal.
See the Java source code of Integer.valueOf() which is reponsible for boxing int values. The javadoc says:
This method will always cache values in the range -128 to 127
So boxed integers in that range are always the same object if they have the same numeric value.
In Kotlin, you should compare boxed Integers with == and not with ===.