two functions test1 and test2, one uses "is" to check type, and one use "as?", seems test2 with "as?" has less code, but is it really better than the one uses "is" to do check?
Is there a comparison for using "is" vs. "as?", what is suggestion for general use of these two?
class Message(val int: Int, val msg:String)
class Test {
fun test1(objList: List<Any?>) {
for (i in objList.size - 1 downTo 0) {
val item = objList.get(i)
if (item is Message) {
println(item)
}
}
}
fun test2(objList: List<Any?>) {
for (i in objList.size - 1 downTo 0) {
(objList.get(i) as? Message)?.let {item ->
println(item)
}
}
}
}
So if you look at the JVM bytecode of both:
// JVM bytecode of test1
L5
LINENUMBER 8 L5
ALOAD 3
INSTANCEOF com/example/artifact/Message
IFEQ L6
// JVM bytecode of test2
L4
LINENUMBER 16 L4
ALOAD 0
ILOAD 1
INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object; (itf)
DUP
INSTANCEOF com/example/artifact/Message
IFNE L5
POP
ACONST_NULL
L5
CHECKCAST com/example/artifact/Message
DUP
IFNULL L6
ASTORE 3
You can clearly see that the test1 does check for the type only and it smart casts the result, while the test2 however first checks if the instance is of that type and then returns null or "casts" explicitly it to that type.
So, through this observation I'll suggest you to use the is operator as it is more optimized for these tasks. If you don't like using curly braces then you can omit them like python, or put the println on the same line as you're doing if conditions.
This is more optimized code if you'd like:
fun test1(objList: List<Any>) {
for (item in objList.asReversed()) {
if (item is Message) println(item)
}
}
fun test2(objList: List<Any>) {
for (item in objList.asReversed()) {
(item as? Message)?.let { println(it) }
}
}
The as? version may have one less line of code, but does it really have less code overall? You may have gotten rid of:
val item = objList.get(i)
But you replaced it with:
(objList.get(i) as? Message)?.let {item ->
In this case, I'd argue that the as? version requires ever so slightly more time to comprehend than the is version, which is more straightforward. I also doubt there's a significant difference in performance between the two approaches. In general, always prefer the more readable code over the more performant code unless you've profiled an actual performance problem in your application. Whether or not there's a performance problem is highly dependent on the context of your application. And remember, premature optimization is the root of all evil.
If you really want less code then consider using the following:
fun test(objList: List<Any>) = objList.asReversed().forEach {
if (it is Message) println(it)
}
This makes use of:
Single-expression functions
List.asReversed (returns a reversed view of the original list)
Iterable.forEach
it: implicit name of a single parameter (in lambda expressions)
is is type checking. as is type casting.
the different is
is return boolean, true or false
as return that type
you use is if you want the answer only yes or no
example
if (user is Login) you just want the answer yes or no right?
and then you do something else
you use as when you want to try to cast to that type
example
(user as? Admin).let{print(it.name.toString())}
this one you want to check if this user is Admin or not
if yes you want to use that user value to print something like name
so for condition Use is
for casting Use as
fun test(objList: List<Any>) = objList.asReversed().forEach {
when(it){ is Message -> println(it)}
}
Related
I am looking for a way to filter a List<Pair<T?, U?>> to a List<Pair<T, U>>: I would like to ignore pairs containing null.
Works, but i need smartcasting, too:
fun <T, U> List<Pair<T?, U?>>.filterAnyNulls() =
filter { it.first != null && it.second != null }
Works, but ugly !!, and does not feel idiomatic at all
fun <T, U> List<Pair<T?, U?>>.filterAnyNulls() = mapNotNull {
if (it.first == null || it.second == null) {
null
} else {
Pair(it.first!!, it.second!!)
}
}
Any nice solutions? And any more generic solutions? (Maybe even in classes which can be deconstructed, like triples or data classes?)
This solution utilizes destructuring and smart casts and will do what you need without ugly !!:
fun <T, U> List<Pair<T?, U?>>.filterPairs(): List<Pair<T, U>> =
mapNotNull { (t, u) ->
if (t == null || u == null) null else t to u
}
I don't think there's a good answer to this, unfortunately.
As you say, the filter() option gives a list of Pair<T?, U?> where the compiler still thinks the pair's values can be null even though we know they can't.
And the mapNotNull() option fixes that, but needs new Pair objects to be created, which is inefficient. (The !! there are ugly, but I think this is one of the times when they're justified. It's a shame that the compiler's smart casts aren't smart enough, but I understand that total type inference is an intractable problem, and it has to stop somewhere.)
I found a third option, which fixes both of those problems, but at the cost of an unsafe cast:
fun <T, U> List<Pair<T?, U?>>.filterNotNull()
= mapNotNull{ it.takeIf{ it.first != null && it.second != null } as? Pair<T, U> }
Like the second option, this uses mapNotNull() to both filter and convert the pairs; however, it does a simple cast to tell the compiler that the type parameters are non-nullable. This returns a Pair<T, U> so that the compiler knows the pair values cannot be null, and it avoids creating any new objects (apart from the overall list, of course).
However, it gives a compile-time warning about an unsafe cast. It runs fine for me (Kotlin/JVM), but like all unsafe casts there's no guarantee it will work on all platforms or all future versions. So it's not ideal.
All three options have drawbacks. Perhaps the best overall is your second option; the code itself is a little ugly, and inefficient (creating new Pairs), but it's safe, will always work, and is nice to use.
(By the way, I think filterNotNull() would be a better name for this function. In the standard library, filter functions seem to be named for what they keep, not what they drop, so filterAnyNulls() would give completely the wrong impression!)
You can first create a function the convert Pair<T?, U?> to Pair<T, U>?. I need to put first and second into some variables to make the smart casting work. You could use first?.let { t -> as well.
Something like this:
fun <T, U> Pair<T?, U?>.toNullablePair() {
val t = first
val u = second
return if (t == null || u == null) null else t to u
}
Then you can create your filter-function using it:
fun <T, U> List<Pair<T?, U?>>.filterAnyNulls() =
mapNotNull { it.toNullablePair() }
This is just a variation on your second suggestion really, but another option is to explicitly return the non-null types:
fun <T, U> List<Pair<T?, U?>>.filterAnyNulls(): List<Pair<T, U>> = this
.filter { (a, b) -> a != null && b != null }
.map { (a, b) -> a!! to b!! }
I was hoping that contracts might be the solution to avoid the !! shouting in the map, but it appears that they are still quite limited and nowhere near developed enough yet. I don't think there's a way to avoid the !! for now, at least until the compiler or contracts get smarter.
I’m trying to use Kotlin’s when block to look up an element in different maps. After confirming the element exists, the code subsequently does not smart-cast the resulting lookup in the map to not null.
Below is a minimum working example: is it possible to rework it such that !! is not needed?
fun main(args: Array<String>) {
val string = "abc"
val map1 = mapOf('a' to 5)
val map2 = mapOf('b' to 4)
when (val char = string.firstOrNull()) {
null -> println("Nothing to find")
in map1 -> println("Found in map1: ${map1[char]!!+1}")
in map2 -> println("Found in map2: ${map2[char]!!-1}")
else -> println("Unrecognised character $char")
}
}
Unfortunately, in Kotlin, functions can't have contracts of the form "if f returns true, then g doesn't return null." Hence, the compiler doesn't use information about definitely successful contains calls.
The workaround with !! is OK in this case because you can be sure that get returns not null. Implementation of complex patterns in when (KT-186) would cover this use case by allowing declaring a variable inside when clauses and providing static guarantees that it's not null.
In Kotlin, I want to do an assignment only if another variable is not null (otherwise, no op). I can think of two succinct ways:
fun main(args: Array<String>) {
var x: Int? = null
var n = 0
// ... do something ...
x?.let { n = it } // method 1
n = x ?: n // method 2
}
However, they don't feel succinct enough, given the frequency I have to do them. The first method seems an overkill. The second method is nagging in requiring an expression after ?:.
I suspect there must be a better way, something like n =? x? Or n = x?? Is there?
Try infix to 'simulate custom infix operations'
// define this
infix fun <T > T.assignFromNotNull(right: T): T = right ?: this
///////////////////////////////////////////////////////////
// Demo using
// Now, Kotlin infix-style
fooA assignFromNotNull fooB
barA assignFromNotNull barB
bazA assignFromNotNull bazB
// Old code, Java if-style
if (fooB != null) {
fooA = fooB;
}
if (barB != null) {
barA = barB;
}
if (bazB != null) {
bazA = bazB
}
There's the following:
val x: Int? = null
val n: Int = x ?: return
This compiles perfectly fine, even though n may not be assigned. Even calls that use n after its 'assignment' are allowed, e.g. println(n), because the compiler only knows that n is Int and that's OK. However, any lines following the assignment will never be called, because we return from the scope. Depending on what you want, that's a no-op. We can't continue because n couldn't be assigned, so just return.
Another option is val n: Int = x!! which will throw a NullPointerException if x == null that should be handled elsewhere. I don't recommend this practice, because Kotlin offers cleaner methods to handle nullability.
For simple check like
if (variable != null) {
doSomething(variable)
}
We could change to
variable?.let { doSometing(it) }
However for a case with else
if (variable != null) {
doSomething(variable)
} else {
doOtherThing()
}
Is there a way of doing so in a single function? Is there something like either?
You can use the elvis-operator ?: like so:
variable?.let { doSomething(it) } ?: doOtherThing()
However personally I think that this gets quite unreadable very quickly. There is nothing wrong with an if-expression.
Another approach which might ring well with functional style is the use of when, which is very close to pattern matching:
when(variable) {
null -> doOtherThingSomething()
else -> doSomething(variable)
}
I would argue, however, that you should be careful with abandoning the classic if statement, if what you're after is its natural purpose - conditional execution. If you're calculating a value in two different ways and then using it in the same way, this approach makes sense. But if your code paths diverge at this point and don't meet again, then if provides a clearer intent.
You can map null-able value if not null by using ternary operator to check not null condition with If...Else Statements.
Here, I had wrote some code snippet to check value null or not ,
Case 1: value initialized
fun main(args: Array<String>) {
val value:Int ?= 10
val mapped = value?.let { "Value is == $value" } ?: "Value not initialized..."
println(mapped)
}
You gotta result: Value is == 10
Case 2: value set remains null
fun main(args: Array<String>) {
val value:Int ?= null
val mapped = value?.let { "Value is == $value" } ?: "Value not initialized..."
println(mapped)
}
You gotta result: Value not initialized...
Consider a class with id field which might be null until stored in database:
class IdableK<T : IdableK<T>> : Comparable<T> {
private var id : Long? = null
}
I am trying to implement a compareTo method as follows:
override fun compareTo(other: T): Int {
if (id == null) {
return -1;
}
if (other.id == null) {
return 1;
}
return id!!.compareTo(other.id!!)
}
Is this a correct way of doing it? Would there be a simple way of doing it?
Check out the kotlin.comparisons package. e.g. You can use compareValues:
class IdableK<T : IdableK<T>> : Comparable<T> {
private var id: Long? = null
override fun compareTo(other: T) = compareValues(id, other.id)
}
This is incorrect. If you have two instances with their ids set to null, both instances will return -1 when you call compareTo(other) on them, while if one returns -1 the other should return 1 in a correct implementation. I'm not sure if there are situations where it makes sense to implement compareTo based on nullable properties, but I can't imagine any. Maybe there's a better way for you too?
Also, you should avoid non-null assertions (!!). Since you're using vars, other threads may change the value to null so that even if you did a null check before, the value is now null and !! throws. Instead, you should store both ids in local variables and check these for null values.
If you absolutely have to use compareTo, I'd do it like this:
override fun compareTo(other: T): Int {
val thisId = id
val otherId = other.id
if (thisId == null && otherId == null) return 0
if (thisId == null && otherId != null) return -1
if (thisId != null && otherId == null) return 1
// thisId and otherId are now smart cast to Long
return thisId.compareTo(otherId)
}
Here is a simple way:
override fun compareTo(other: T) :Int {
return id?.compareTo(other.id ?: return 1) ?: -1
}
However this piece of code is very unfriendly to a novice kotlin programmer. It involves too much magic that make it look like scala. These 3 question marks make people puzzled, at least they must think for a minute or two before they could realize what is going on in this minimalistic one-liner. I still prefer your edition. It's more verbose, but clear.
And I'm really worried about the symmetric problem. This matters, and isn't just a design problem. If you don't compare nullable properties, there won't be this programming puzzle. It will just be override fun compareTo(other: T) = id.compareTo(other.id). Simple, clear, and no misleading.
I would rather throw away all null checking code and just live with those null assertions. Because mostly you won't compare there things until it is fully initialized. If these assertion fails, it means something really bad has happens.
Oh, BTW, I don't know about your project, and if it hits the rare cases that you have to compare nullable properties, I think you could write a special edition of Comparator that consider nulls instead of throwing NPEs. Don't mess with the natural order.