-1

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)
            }
        }
    }
}
lannyf
  • 9,865
  • 12
  • 70
  • 152
  • 2
    @user you may want to use backticks next time to make code (or keywords) look `like this` – Marcin Orlowski Apr 24 '20 at 00:21
  • 2
    The `as?` version may have one less line, but is it _really_ less code overall? I'd argue the `is` version is cleaner and more readable, thus better. If you want less code then remember that lists can be accessed with `list[i]` instead of `list.get(i)` and you can make use of https://kotlinlang.org/docs/reference/lambdas.html#it-implicit-name-of-a-single-parameter. And if your function becomes more complicated don't forget about https://kotlinlang.org/docs/reference/control-flow.html#when-expression, though if you have cascading `is` checks you may want to rethink your design. – Slaw Apr 24 '20 at 01:16
  • 1
    You could also use: `fun test(objList: List) = objList.asReversed().forEach { if (it is Message) println(it) }`. See https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/as-reversed.html – Slaw Apr 24 '20 at 01:17
  • @Slaw for is somewhat more optimized than extension `forEach{}` see https://stackoverflow.com/questions/52904923/slow-range-foreach-in-kotlin :^), your code us clean btw but this makes more sense, `fun test(objList: List) = for(item in objList.asReversed()) { if (item is Message) println(it) }` – Animesh Sahu Apr 24 '20 at 03:18
  • @AnimeshSahu For an `Iterable` there should be no difference between `for (x in y)` and `y.forEach`. In fact, the extension function is just `for (e in this) action(e)`—and it's an _inline function_, so there should literally be no difference in compiled code. My own benchmarks, when using a proper framework (i.e. JMH), show no statistical difference in average execution time. And ehhh, I find my version makes more sense as it's more straight forward when reading left-to-right. But that's why I voted to close this question as primarily opinion-based. – Slaw Apr 24 '20 at 06:06
  • @Slaw ahh, that's great – Animesh Sahu Apr 24 '20 at 06:35
  • @Slaw, you have a strong argument and that's is what type of answer I am looking for, if you put your comment as an Answer I would take it. Thanks! – lannyf Apr 24 '20 at 11:43

3 Answers3

3

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) }
    }
}
Animesh Sahu
  • 7,445
  • 2
  • 21
  • 49
  • good to look at the bytecode. for this sample it is clear that "is" is better than "as?" although looks the test2 has one line less. I was looking for a general opinion for how to look at "is" vs. "as?". btw, the curly bracket block is just for "there are more code in it" not just really only online. Thanks! – lannyf Apr 24 '20 at 11:50
1

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:

Slaw
  • 37,820
  • 8
  • 53
  • 80
1

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)}
}