0

I'm using the Epoxy library on Android.

What I'm curious about is why the parameter of the lambda expression doesn't get an error when the type doesn't match.

The listener is a lambda expression that takes an Int type as a parameter.

But listener(addDetailClicked) works normally.

Shouldn't it be listener(Int)? or listener({ i -> addDetailClicked(i) }).

Actually, I don't know why it works even after I write the code.

How is this possible?

Model

@EpoxyModelClass(layout = R.layout.item_routine)
abstract class EpoxyRoutineModel() : EpoxyModelWithHolder<EpoxyRoutineModel.Holder>() {
    @EpoxyAttribute
    var workout: String = "see"

    @EpoxyAttribute
    var curPos: Int = 0

    @EpoxyAttribute
    lateinit var listener: (Int) -> Unit // this

    override fun bind(holder: Holder) {
        holder.workout.text = workout
        holder.add_btn.setOnClickListener {
            listener(curPos)
        }
    }
}

Controller

class RoutineItemController(
    private val addDetailClicked: (Int) -> Unit)
    : EpoxyController() {
    private var routineItem : List<RoutineItem>? = emptyList()

    override fun buildModels() {
        var i:Int =0
        routineItem?.forEach {
            when(it) {
               is RoutineItem.RoutineModel ->
                   EpoxyRoutineModel_()
                       .id(i++)
                       .curPos(i++)
                       .workout("d")
                       .listener(addDetailClicked) // why? listener(Int) or listener({ i -> addDetailClicked(i) })
                       .addTo(this)
           }

        }
    }
}

Fragment

class WriteRoutineFragment : Fragment() {
    private var _binding : FragmentWriteRoutineBinding? = null
    private val binding get() = _binding!!
    private lateinit var epoxyController : RoutineItemController
    private val vm : WriteRoutineViewModel by viewModels { WriteRoutineViewModelFactory() }

    override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        _binding = FragmentWriteRoutineBinding.inflate(inflater, container, false)

        epoxyController = RoutineItemController(::addDetail)
        binding.rv.adapter = epoxyController.adapter
        binding.rv.itemAnimator = null

        return binding.root

    }
    private fun addDetail(pos: Int) {
        vm.addDetail2(pos)
    }
}
ybybyb
  • 1,385
  • 1
  • 12
  • 33

1 Answers1

1

I believe you missed the fact that EpoxyRoutineModel_ contains setters for data types found in EpoxyRoutineModel. For example, EpoxyRoutineModel.curPos is of type Int, so EpoxyRoutineModel_.curPos() is a function declared as:

fun curPos(Int): EpoxyRoutineModel_

(or similar)

Similarly, EpoxyRoutineModel.listener is of type (Int) -> Unit, so EpoxyRoutineModel_.listener() is declared as:

fun listener((Int) -> Unit): EpoxyRoutineModel_

So listener() is a function that receives another function (which itself receives Int). So we can provide addDetailClicked there.

broot
  • 21,588
  • 3
  • 30
  • 35
  • then you're saying that it's the same thing as `Java's user.setcurPos()`, `user.setName()`, `user.setNumber()`, etc...? – ybybyb Oct 19 '21 at 19:07
  • 1
    I don't have the code of `EpoxyRoutineModel_`, but it seems so. For example, `curPos(i++)` clearly shows that `curPos()` is a function receiving `Int`, so it looks like some kind of a setter. – broot Oct 19 '21 at 19:10
  • So, the reason `listener(addDetailClicked)` is possible is that `listener()` is not a function call that takes an `Int` as a `parameter`, but simply calls the `settter function` of the `listener` to initialize the `listener property`? So, is it possible to pass `addDetailClicked` of `(Int) -> Unit` appropriate to the type of `listener property` as an `argument`? – ybybyb Oct 19 '21 at 19:11
  • 1
    It seems so, yes. If `curPos()` is a setter for `Int`, then similarly, `location()` should be a setter for `(Int) -> Unit` function. Which actually makes sense, because you use `EpoxyRoutineModel_` to build `EpoxyRoutineModel`. The latter contains the property for `(Int) -> Unit` function, so the builder have to provide a way to set it. – broot Oct 19 '21 at 19:16
  • 1
    You should be able to ctrl+click on `.listener(addDetailClicked)`. I guess you will see a function declared something like this: `fun listener((Int) -> Unit): EpoxyRoutineModel_`. This will confirm that this function actually receives another function. – broot Oct 19 '21 at 19:17
  • It's a little different, but it's true. Actually `public EpoxyRoutineModel_ listener(Function1 super Integer, Unit> listener) { onMutation(); super.listener = listener; return this; }` Here `super.listener = listener` seems to mean it. And curPos also `public EpoxyRoutineModel_ curPos(int curPos) { super.setCurPos(curPos); return this; }` As you said, these functions exist. After all, it was a `setter`! – ybybyb Oct 19 '21 at 19:49