1

In Code A1 I have used the let statement, so I think it will not be null with filenameofVideo.path

But I get the following error, why?

Smart cast to 'File' is impossible, because 'filenameofVideo' is a mutable property that could have been changed by this time

At this moment, I have to use Code A2.

Code A1

private var filenameofVideo :File?=null

filenameofVideo?.let {
          Navigation.findNavController(requireActivity(), R.id.fragment_container)
               .navigate(UIFragmentCameraDirections.actionCameraToVideo(filenameofVideo.path))
}

Code A2

private var filenameofVideo :File?=null

filenameofVideo?.let {filenameofVideo ->
        Navigation.findNavController(requireActivity(), R.id.fragment_container)
            .navigate(UIFragmentCameraDirections.actionCameraToVideo(filenameofVideo.path))
}

And more, I find both Code B1 and Code B2 are correct. Why is Code B1 correct and Code A1 wrong?

Code B1

   private val aa:String?=null
   aa?.let {
            print(aa)
        }

Code B2

private val aa:String?=null    
   aa?.let{aa->
           print(aa)
      }

Added Content:

1: In Code C, the var aa might have been changed (perhaps by another thread) between the moment it is accessed in the ?.let call and the moment it is accessed within the let block.

Code C will be launched when aa is not null, and Code C will not launched when aa is null, right?

2: In Code D (I assume the compiler accept it ), the function always be launched no matter aa is null or not, it can not be accepted, so the system will interrupt, right?

Code C

private var aa: String? = null    
aa?.let { kk ->
    print(kk.length)
}

Code D

private var aa: String? = null    
aa?.let {
    print(aa.length)
}
HelloCW
  • 843
  • 22
  • 125
  • 310
  • 1
    Possible duplicate of [Smart cast to 'Type' is impossible, because 'variable' is a mutable property that could have been changed by this time](https://stackoverflow.com/questions/44595529/smart-cast-to-type-is-impossible-because-variable-is-a-mutable-property-tha) – Tenfour04 Nov 05 '19 at 02:34
  • Thanks!but Code A has used safe call `let` – HelloCW Nov 05 '19 at 03:35
  • But you are using the property again instead of “it”. – Tenfour04 Nov 05 '19 at 04:36
  • Thanks! But you can't use `it` because it's nest lamabad expression – HelloCW Nov 05 '19 at 05:26
  • Simply name the parameter instead of using `it` then. – Alexey Romanov Nov 05 '19 at 07:50
  • Use `filenameofVideo?.let {filenameofVideo -> //...` and it will shadow its own property so you can use `filenameofVideo` within the `let` block after all. – Tenfour04 Nov 05 '19 at 13:39
  • Thanks! your code `filenameofVideo?.let {filenameofVideo -> //... ` works well, but why doesn't the Code A work? – HelloCW Nov 05 '19 at 14:25
  • I think `filenameofVideo?.let {filenameofVideo -> //... ` is the same as Code A. – HelloCW Nov 05 '19 at 14:26

2 Answers2

2

Let's trim your code down a bit so that it's a little easier to discuss:

private var filename :File?=null

filename?.let {
    action(filename.path)
}

The problem here is that, even though you've called ?.let, the compiler cannot guarantee that filename is not null inside the let block. This is because filename is a var, and the value of the var might have been changed (perhaps by another thread) between the moment it is accessed in the ?.let call and the moment it is accessed within the let block.

Therefore, you must handle the possibility of null yourself. The easiest way to do this will be to use the it value inside the let block, or to name that value yourself:

filename?.let { safe ->
    action(safe.path)
}

The reason that this code does work:

private val aa:String?=null
aa?.let {
    print(aa)
}

is that here the variable is a val. This means that it will either be null or non-null, but either way it can't change. So you and I can read the code and see that the .let block will never be executed... but if somehow it were, that would mean that aa is guaranteed to not be null, and so the compiler can smart-cast it to a non-nullable type.

Ben P.
  • 52,661
  • 6
  • 95
  • 123
1

The code like

private var aa: String? = null
aa?.let {
    print(aa.length)
}

does not compile because, while the safe call ?.let { } block is executed only if aa property is not null, by the time the execution reaches aa.length, the value of aa property might have been already changed to null in another thread.

On the other hand, in this example

private var aa: String? = null    
aa?.let { aa ->
    print(aa.length)
}

the non-null value of aa property is captured after the safe call in the read-only aa parameter of the lambda passed to let.

Note that making aa property val instead of var as in your example B1 also helps to eliminate the error, but that causes the let block be never executed since a private val initialized to null can never become non-null.

As for the additional question about what is a difference between the examples C and D:

  • there's no difference in how the let { } function is executed. In both cases the lambda function is launched only when the receiver expression aa is not null, because you use ?. safe call operator.
  • the example D isn't accepted by the compiler because it can't ensure the value of aa property referenced inside lambda hasn't been changed by the time execution reaches that line.
Ilya
  • 21,871
  • 8
  • 73
  • 92