15

Is it a good practice to use multiple nested let in Kotlin or should I introduce a local variable instead? Is there any overhead?

webView?.let { webview ->
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        webview.setLayerType(View.LAYER_TYPE_HARDWARE, null)
    } else {
        webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
    }

    webview.webViewClient = WebViewClient()

    webview.settings.let { settings ->
        settings.javaScriptEnabled = true
        settings.setSupportZoom(false)
        settings.builtInZoomControls = false
        settings.displayZoomControls = false
        settings.loadsImagesAutomatically = true
    }
}
Yogesh Umesh Vaity
  • 41,009
  • 21
  • 145
  • 105
Sparrow318
  • 468
  • 2
  • 5
  • 15
  • 1
    hint: you can use `let` without `->` by referring to object by `it` – pwolaq Feb 16 '18 at 13:59
  • 3
    @pwolaq But because they're nested, you'd have to be VERY careful as you could accidentally reference the wrong 'it'. If you're going to nest let,run,apply,with,also, ensure you explicitly name things (as Sparrow318 did). – Mikezx6r Feb 16 '18 at 22:55
  • 1
    @Mikezx6r I disagree with you, it would be a problem only if the outer object would have same methods/properties that you would like to use on the inner object - in other cases IDE would hint you that something is wrong :) – pwolaq Feb 17 '18 at 07:11
  • @pwolaq. I spoke too strongly, but I think we agree that it can be risky to rely on 'it' everywhere. You won't get a warning IF there is a property that exists on both and you intended to reference the outer, rather than the inner. If you know there's no overlap, go for it, but be aware of the risks. – Mikezx6r Feb 17 '18 at 15:44
  • 1
    i got used to naming the value, for the same reason you don't declare a variable as "temp1". Especially with nested lets it's much easier to revisit old code. – Flo We Feb 19 '18 at 14:02

4 Answers4

13

Since let is inlined (see: what is inlining, also the official docs), its cost is the exact same as declaring the local variable yourself. If you're using it with a safe call, it also adds a null check, which you'd have to do manually as well.

So there's basically no overhead to using it, feel free to use it as much as you want as long as your code is legible enough for you.


For example, code like this:

webview.settings.let { settings ->
    settings.javaScriptEnabled = true
    settings.setSupportZoom(false)
}

... would roughly translate to bytecode equivalent to this Java code:

Settings settings = webview.getSettings();
settings.setJavaScriptEnabled(true);
settings.setSupportZoom(false);

Except settings would probably be called something generic like var10000, but that's besides the point. Point is that no function instances are created and no let function is called or anything like that, therefore no overhead.

For any future concerns, you can actually check this yourself by using the Kotlin plugin's bytecode viewer and decompiler.

zsmb13
  • 85,752
  • 11
  • 221
  • 226
4

Kotlin's creators recommend not to use too many nested let calls to achieve better code readability and conciseness. In their book 'Kotlin in Action' they say

When you need to check multiple values for null, you can use nested let calls to handle them. But in most cases, such code ends up fairly verbose and hard to follow. It’s generally easier to use a regular if expression to check all the values together.

Yogesh Umesh Vaity
  • 41,009
  • 21
  • 145
  • 105
1

A side remark. The inner let block could be rewritten for better readability with run:

webview.settings.run {
    javaScriptEnabled = true
    setSupportZoom(false)
    builtInZoomControls = false
    displayZoomControls = false
    loadsImagesAutomatically = true
}
Inego
  • 1,039
  • 1
  • 12
  • 19
1

As an addition to zmbs13's answer:

Since you don't need a null check for webview.settings, it would be better to use apply which takes a lambda with receiver:

webview.settings.apply {
    // you refer to webview.settings with 'this', which can be omitted
    this.javaScriptEnabled = true // using this explicitely, in case there is another variable 'javaScriptEnabled' in this block
    setSupportZoom(false) // omitting this from here on
    builtInZoomControls = false
    displayZoomControls = false
    loadsImagesAutomatically = true
}

This makes your code even more concise and avoids the redundancy of having to write settings, or it multiple times.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121