androidkotlinepoxy

In the code, the type of the lambda is mismatched, how does it work?


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

Solution

  • 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.