В коде тип лямбды не соответствует, как это работает?

#android #kotlin #epoxy

Вопрос:

Я пользуюсь Epoxy library включенным Android .

Что мне любопытно, так это то, почему parameter из lambda expression них не появляется ошибка, когда тип не совпадает.

Это listener a lambda expression , которое принимает an Int type в качестве параметра.

Но listener(addDetailClicked) работает нормально.

Разве этого не должно быть listener(Int) ? или listener({ i -> addDetailClicked(i) }) .

На самом деле, я не знаю, почему это работает даже после того, как я напишу код.

Как это возможно?

Модель

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

Контроллер

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

        }
    }
}
 

Фрагмент

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

Ответ №1:

Я полагаю, вы упустили тот факт, что EpoxyRoutineModel_ содержит установщики для типов данных, найденных в EpoxyRoutineModel . Например, EpoxyRoutineModel.curPos имеет тип Int , поэтому EpoxyRoutineModel_.curPos() функция объявлена как:

 fun curPos(Int): EpoxyRoutineModel_
 

(или что-то подобное)

Аналогично, EpoxyRoutineModel.listener имеет тип (Int) -> Unit , поэтому EpoxyRoutineModel_.listener() объявляется как:

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

Так listener() же и функция, которая получает другую функцию (которая сама получает Int ). Так что мы можем предоставить addDetailClicked там.

Комментарии:

1. тогда вы говорите, что это то же самое Java's user.setcurPos() , user.setName() что, user.setNumber() , и т. Д.?

2. У меня нет кода EpoxyRoutineModel_ , но, похоже, это так. Например, curPos(i ) четко видно , что curPos() это функция приема Int , поэтому она выглядит как своего рода сеттер.

3. Итак, причина listener(addDetailClicked) возможна в том , что listener() это не вызов функции, которая принимает an Int как a parameter , а просто вызывает settter function функцию listener для инициализации listener property ? Итак, можно ли передать addDetailClicked (Int) -> Unit соответствующий тип listener property в качестве argument ?

4. Похоже на то, да. Если curPos() является задатчиком для Int , то аналогичным образом location() должен быть задатчик для (Int) -> Unit функции. Что на самом деле имеет смысл, потому что вы используете EpoxyRoutineModel_ для сборки EpoxyRoutineModel . Последний содержит свойство для (Int) -> Unit функции, поэтому конструктор должен предоставить способ его установки.

5. Вы должны иметь возможность нажать ctrl .listener(addDetailClicked) . Я думаю, вы увидите функцию, объявленную примерно так: fun listener((Int) -> Unit): EpoxyRoutineModel_ . Это подтвердит, что эта функция действительно получает другую функцию.