Можно ли создать рекурсивный тип функции в Kotlin?

у меня есть функции, которые представляют собой шаги в процессе. Каждая функция также знает следующий шаг, если он есть. Я хотел бы иметь возможность сделать что-то вроде:

fun fooStep() : Step? {
    ... do something ...
    return ::barStep // the next step is barStep
}

эти функции вызываются из Центральной диспетчерской функции, которая содержит код примерно так:

var step = startStep
while (step != null) {
    step = step()
}

обратите внимание, что логика на определенном шаге также определяет следующий шаг, если оно вообще существует.

Я думал, что смогу определить Step as:

typealias Step = () -> Step?

так Step - это функция, которая возвращает другой Step, или null. Однако это не удается скомпилировать с помощью:

Kotlin: Recursive type alias in expansion: Step

Я могу обойти это, обернув функцию в объект. например:

data class StepWrapper(val step: () -> StepWrapper?)

и изменение сигнатур моей функции соответственно.

к сожалению, это означает, что я не могу просто использовать функции литералы (например: ::barStep), но вместо этого нужно обернуть их в StepWrapper:

fun fooStep() : StepWrapper? {
    ... do something ...
    return StepWrapper(::barStep)
}

(Я тоже соответственно, я должен изменить цикл отправки.)

Я хотел бы избежать необходимости создавать эти объекты-оболочки, если это возможно. Есть ли способ сделать это в Котлине?

3 ответов


вы можете определить его, используя некоторый общий интерфейс:

interface StepW<out T> : ()->T?

interface Step : StepW<Step>


class Step1 : Step {
    override fun invoke(): Step? = Step2()
}

class Step2 : Step {
    override fun invoke(): Step? = null
}

здесь Step ваш тип рекурсивной функции.


вот как вы можете заставить его работать, хотя я очень не уверен, что вы пытаетесь достичь с его помощью:

typealias Fun<T> = () -> T
typealias Step<T> = () -> (T)

typealias Step1 = Step<Fun<Step2>>
typealias Step2 = Step<Fun<Step3>>
typealias Step3 = Step<Unit>

fun step1(): Step1 {
    return {
        println("step 1")
        ::step2
    }
}

fun step2(): Step2 {
    return {
        println("step 2")
        ::step3
    }
}

fun step3(): Step3 {
    return { println("done") }
}

использовать перечисление реализовать шаблон состояния с конечными состояниями и предпочитают возвращать ненулевые значения. Перечисление может наследовать функцию.

enum class Step : () -> Step {
    Step1 {
        override fun invoke() = Step2
    },
    Step2 {
        override fun invoke() = End
    },
    End {
        override fun invoke() = this
    }
}

fun work() {
    var step = Step.Step1
    while (step !== Step.End) {
        step = step()
    }
}