1

Тема: Kotlin. Generics

Добрий вечір. Допоможіть, будь ласка. Перше завдання було неважке, тому друге напевно також має бути легке, але я не можу його зробити. Entities.kt і ResultPrinter.kt допоміжні файли для Task2Transformer.kt.
Опис завдань є в коді, в коментарях.
Як я зрозумів: Transformer - це екземпляр операції. Всі операції зв'язані так як і в LinkedList, тобто посиланням на наступний об'єкт іншого типу - next, а також операція яка приведе до цього типу - action, і сама операція - onNext.

Файл Entities.kt

/**
 * Some data classes for running examples of Transformer Task
 */

data class State(
        val data: Data? = null,
        val error: Error? = null
)

data class Error(
        val code: Int = 0,
        val message: String? = null
)

data class Data(val value: Number)

Файл ResultPrinter.kt

/**
 * Class which prints results of optional values
 * If value exists (non-null) it prints: "No value"
 * Otherwise calls standard toString method
 */
open class ResultPrinter<T : Any>(private val value: Optional<T>) {
    open fun print() {
        if (value.exists) {
            println(value.get())
        } else {
            println("No value")
        }
    }
}

Файл Task1Optional.kt

/**
 * TODO Write generic class Optional which allows
 * - to check whether [value] which it was created with [exists] or null
 * - retrieve value if it is not null [get]
 * - in case value is null get should cause NullPointerException
 * - retrieve nullable version of value [getOrNull]
 *
 * There should be a companion object extension method [toOptional] which allows
 * to create Optional object from `Any?` object
 */
class Optional <T> private constructor(
    private val value: T?
) {
    val exists: Boolean = value != null

    fun get() = if (exists) value else throw NullPointerException()

    fun getOrNull() = if (exists) value else null

    companion object {
        fun <T> T.toOptional(): Optional<T> = Optional(this)
    }
}

Файл Task2Transformer.kt

import com.company.generics.Optional.Companion.toOptional

/**
 * TODO Finish Transformer class
 * This class allows to chain several conversion operations.
 * Whenever new value is sent to provided [source] the whole chain should made appropriate conversions
 * and return expected result in lambda provided to [transform] method.
 *
 * Transformer class does not support nullable values (same as Observable in RxJava).
 * Otherwise NullPointerException will be thrown during conversions.
 *
 * For convenience [Optional] type is used and results are printed using [ResultPrinter]
 *
 * Please check [main] method for the reference how it should work
 */
class Transformer<T>(private val source: Source<T>) {

    init {
        source.subscribe(this)
    }

    private var onNext: /*TODO Add type*/ = null
    private var next: Transformer<Any>? = null
    private var action: (Any) -> T = { it as T }

    fun transform(onNext: /*TODO Add type*/) {
        this.onNext = onNext
    }

    fun map(/*TODO Add generic argument*/): Transformer</*Another type than T*/> {
        return TODO("Write implementation")
    }

    private fun next(value: T) {
        val next = next
        if (next == null) {
            onNext?.invoke(action(value!!))
        } else {
            next.source.send(action(value!!)!!)
        }
    }

    class Source<S> {
        private var listener: Transformer<S>? = null

        fun subscribe(transformer: Transformer<S>) {
            this.listener = transformer
        }

        fun send(value: S) {
            listener?.next(value)
        }
    }
}

/**
 * Code below is a specification and should compile and work as described
 */
fun main() {
    val source = Transformer.Source<State>()
    Transformer(source)
            .map { it.data.toOptional() }
            .transform {
                ResultPrinter(it).print()
            }
    source.send(State()) // -> "No value"
    source.send(State(data = Data(1))) // -> "Data(value=1)"

    val errorSource = Transformer.Source<Error>()
    Transformer(errorSource)
            .map { it.message.toOptional() }
            .transform {
                ResultPrinter(it).print()
            }
    errorSource.send(Error()) // -> "No value"
    errorSource.send(Error(message = "Generics are awesome")) // -> "Generics are awesome"

    Transformer(source)
            .map { (it.error?.message).toOptional() }
            .transform {
                ResultPrinter(it).print()
            }
    source.send(State()) // -> "No value"
    source.send(State(error = Error(0, null))) // -> "No value"
    source.send(State(
            error = Error(0, "Generics are awesome")
    )) // -> "Generics are awesome"
}

Дякую за витрачений час.