2

The Code A is from the article.

It' wrapped with with, I hope to convert it to a full code, how can I do?

BTW, Code B is wrong.

Code A

val pxValue = with(LocalDensity.current) { 16.dp.toPx() }

Code B

val s = LocalDensity.current
val pxValue=s.16.dp.toPx()

Add Content:

For example, the Code C is using with, I can write a full Code D instead of Code C without with.

Code C

data class Person(var name: String, var tutorial : String)
var person = Person("Anupam", "Kotlin")

with(person) {
    name = "No Name"
    tutorial = "Kotlin tutorials"
}

Code D

data class Person(var name: String, var tutorial : String)
var person = Person("Anupam", "Kotlin")

person.name = "No Name"
person.tutorial = "Kotlin tutorials"
HelloCW
  • 843
  • 22
  • 125
  • 310
  • You can't use `toPx()` without `with()` or something similar. You would have to use more classic ways to convert it, without using Kotlin extensions (I'm not familiar with Android to say what it is). – broot Aug 03 '22 at 06:38
  • Do you mean sth like: `val px = LocalDensity.current.apply { this.16.dp.toPx() }` – LenglBoy Aug 03 '22 at 07:06

1 Answers1

4

Why can you rewrite Code C into Code D, but not Code A into Code B? The difference, as the answer your linked also mentioned, is that toPx in Code A is an extension method of Dp declared in Density. It is declared like this:

open fun Dp.toPx(): Float

On the other hand, none of the properties you use in the with block in Code C are extension properties declared in Person.

So to call toPx, not only do you need an instance of Density as the dispatch receiver, you also need an instance of Dp as the extension receiver. (See this for more info about dispatch vs extension receivers)

With scope functions that change what the implicit receiver this means (e.g. with/run), you can easily provide both of these receivers:

with(LocalDensity.current) {  // LocalDensity.current: (implicit) dispatch receiver
    16.dp.toPx() // 16.dp: explicit extension receiver
}

It is possible (but not very intuitive or convenient) to provide both receivers without using scope functions. For example, and for academic purposes only, you can declare additional extension functions on Density:

fun Density.dpToPx(x: Int) = x.dp.toPx()
fun Density.dpToPx(x: Float) = x.dp.toPx()
fun Density.dpToPx(x: Double) = x.dp.toPx()

Here the dispatch receiver of x.dp.toPx is the same as the extension receiver of the newly declared Density.toPx, and the extension receiver is x.dp.

Then you can call this like this:

LocalDensity.current.dpToPx(16)

I strongly recommend you to use with when writing real code.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • I read this question and answer with great interest and was thinking, how could this be done without Kotlin 1.7's new Context Receiver feature? TIL. Thanks. – k314159 Aug 03 '22 at 08:10
  • @k314159 I don't know too much about that feature, but from what I have heard it is used to provide implicit receivers, and it even makes it possible to provide *multiple* ones (?), so it definitely sounds like this feature can be used to do this. Feel free to write another answer showing another way! :) – Sweeper Aug 03 '22 at 08:15
  • @Sweeper I believe even with context receivers feature, the standard way is still to use `with` or other scoping function. I don't understand why OP would like to avoid it. – broot Aug 03 '22 at 08:41
  • @broot Yes, of course. I did not mean to imply otherwise. I was just also interested in how the new feature could be used. I think OP was just trying to understand what's going here (probably confused by the two receivers), and they thought rewriting it without `with` was a good idea to do that. – Sweeper Aug 03 '22 at 08:42