Mobile Development 16 min read

Unlock Peak Kotlin Performance with Advanced Refactoring Techniques

This article guides developers through a series of Kotlin refactoring steps—replacing when statements with HashMap, introducing payload mechanisms, infix functions, inline registration, and delegated properties—to dramatically improve event‑handling performance, readability, and maintainability while adhering to the single‑responsibility principle.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Unlock Peak Kotlin Performance with Advanced Refactoring Techniques

In the world of software development, refactoring is the hero that rescues code from inefficiency. This tutorial embarks on a Kotlin adventure to refactor event‑handling code, aiming for higher performance, cleaner style, and easier maintenance.

Our Goal

We aim to transform Kotlin event handling by introducing HashMap‑based dispatch, payload mechanisms, infix functions, inline registration, and delegated properties, all while following the single‑responsibility principle.

Step 1: Baseline Code

The original implementation uses a when statement to handle a sealed BlockEvent hierarchy:

open fun onEvent(event: Event) {
    // ...
    handleBlockEvent(engine, getBlockForEvents(), checkNotNull(assetsRepo.fontFamilies.value).getOrThrow())
}

fun handleBlockEvent(engine: Engine, block: DesignBlock, fontFamilyMap: Map<String, FontFamilyData>, event: BlockEvent) {
    when (event) {
        BlockEvent.OnDelete -> engine.delete(block)
        BlockEvent.OnBackward -> engine.sendBackward(block)
        BlockEvent.OnDuplicate -> engine.duplicate(block)
        BlockEvent.OnForward -> engine.bringForward(block)
        BlockEvent.ToBack -> engine.sendToBack(block)
        BlockEvent.ToFront -> engine.bringToFront(block)
        BlockEvent.OnChangeFinish -> engine.editor.addUndoStep()
        is BlockEvent.OnChangeBlendMode -> onChangeBlendMode(engine, block, event.blendMode)
        is BlockEvent.OnChangeOpacity -> engine.block.setOpacity(block, event.opacity)
        is BlockEvent.OnChangeFillColor -> onChangeFillColor(engine, block, event.color)
        // and so on...
    }
}

sealed class BlockEvent : Event {
    object OnChangeFinish : BlockEvent
    object OnForward : BlockEvent
    object OnBackward : BlockEvent
    object OnDuplicate : BlockEvent
    object OnDelete : BlockEvent
    object ToFront : BlockEvent
    object ToBack : BlockEvent
    data class OnChangeBlendMode(val blendMode: BlendMode) : BlockEvent
    data class OnChangeOpacity(val opacity: Float) : BlockEvent
    data class OnChangeFillColor(val color: Color) : BlockEvent
    // and so on...
}

Step 2: Replace when with HashMap

We introduce a mutable map that links each event class to its handling lambda, eliminating the O(n) when lookup and achieving O(1) dispatch:

abstract class EventsHandler<Payloads>(
    val fillPayload: (cache: Payloads) -> Unit
) {
    abstract val payloadCache: Payloads
    private val eventMap = mutableMapOf<KClass<out Event>, Payloads.(event: Event) -> Unit>()

    fun handleEvent(event: Event) {
        eventMap[event::class]?.let {
            it.invoke(payloadCache.also { fillPayload(it) }, event)
        }
    }

    operator fun <EventType : Event> set(event: KClass<out EventType>, lambda: Payloads.(event: EventType) -> Unit) {
        eventMap[event] = lambda as Payloads.(event: Event) -> Unit
    }
}

class BlockEventsHandler(fillPayload: (cache: BlockEventsHandler.Payloads) -> Unit) : EventsHandler<BlockEventsHandler.Payloads>(fillPayload) {
    class Payloads {
        lateinit var engine: Engine
        lateinit var block: DesignBlock
        lateinit var fontFamilyMap: Map<String, FontFamilyData>
    }
    override val payloadCache: Payloads = Payloads()

    init {
        it[BlockEvent.OnDelete::class] = { engine.delete(block) }
        it[BlockEvent.OnBackward::class] = { engine.sendBackward(block) }
        it[BlockEvent.OnDuplicate::class] = { engine.duplicate(block) }
        it[BlockEvent.OnForward::class] = { engine.bringForward(block) }
        it[BlockEvent.ToBack::class] = { engine.sendToBack(block) }
        it[BlockEvent.ToFront::class] = { engine.bringToFront(block) }
        it[BlockEvent.OnChangeFinish::class] = { engine.editor.addUndoStep() }
        it[BlockEvent.OnChangeBlendMode::class] = { onChangeBlendMode(engine, block, it.blendMode) }
        it[BlockEvent.OnChangeOpacity::class] = { engine.block.setOpacity(block, it.opacity) }
        it[BlockEvent.OnChangeFillColor::class] = { onChangeFillColor(engine, block, it.color) }
        // and so on...
    }
}

private val blockEventHandler = BlockEventsHandler {
    it.engine = engine
    it.block = getBlockForEvents()
    it.fontFamilyMap = checkNotNull(assetsRepo.fontFamilies.value).getOrThrow()
}

open fun onEvent(event: Event) {
    // ...
    blockEventHandler.handleEvent(event)
}

This change yields constant‑time dispatch and bundles required data into a payload object for clearer, safer code.

Step 3: Add an Infix to Function

We define an infix extension that maps a KClass to its handling lambda, making the registration syntax more expressive:

abstract class EventsHandler<Payloads>(
    val fillPayload: (cache: Payloads) -> Unit
) {
    infix fun <Payloads, EventType : Event> KClass<out EventType>.to(lambda: Payloads.(event: EventType) -> Unit) {
        eventMap[event] = lambda as Payloads.(event: Event) -> Unit
    }
    // ... (rest unchanged)
}

class BlockEventsHandler(
    manager: EventsManager,
    override val fillPayload: (cache: TextBlockEventsHandler) -> Unit
) : EventsHandler<TextBlockEventsHandler>(manager) {
    lateinit var engine: Engine
    lateinit var block: DesignBlock
    lateinit var fontFamilyMap: Map<String, FontFamilyData>

    init {
        BlockEvent.OnDelete::class to { engine.delete(block) }
        BlockEvent.OnBackward::class to { engine.sendBackward(block) }
        BlockEvent.OnDuplicate::class to { engine.duplicate(block) }
        BlockEvent.OnForward::class to { engine.bringForward(block) }
        BlockEvent.ToBack::class to { engine.sendToBack(block) }
        BlockEvent.ToFront::class to { engine.bringToFront(block) }
        BlockEvent.OnChangeFinish::class to { engine.editor.addUndoStep() }
        BlockEvent.OnChangeBlendMode::class to { onChangeBlendMode(engine, block, it.blendMode) }
        BlockEvent.OnChangeOpacity::class to { engine.block.setOpacity(block, it.opacity) }
        BlockEvent.OnChangeFillColor::class to { onChangeFillColor(engine, block, it.color) }
        // ...
    }
}

Step 4: Inline Registration without ::class

We create an inline register function with a reified type parameter, allowing registration without explicitly writing ::class:

class EventsHandler(
    register: EventsHandler.() -> Unit,
) {
    inline fun <reified EventType : BaseEvent> register(noinline lambda: (event: EventType) -> Unit) : Any {
        this[EventType::class] = lambda
        return lambda
    }
    // ... (rest unchanged)
}

register<BlockEvent.OnChangeLineWidth> {
    engine.block.setWidth(block, engine.block.getFrameWidth(block))
    engine.block.setHeight(block, it.width)
}

Step 5: Make register an Extension Function

Moving register outside the class highlights it as an extension, improving readability while keeping performance unchanged:

class EventsHandler(
    register: EventsHandler.() -> Unit,
) { /* unchanged */ }

inline fun <reified EventType : BaseEvent> EventsHandler.register(noinline lambda: (event: EventType) -> Unit) : Any {
    this[EventType::class] = lambda
    return lambda
}

Step 6: Replace lateinit with Delegated Properties

We introduce an Inject delegate that lazily provides dependencies, removing the need for lateinit variables and making the payload creation more declarative:

class Inject<Type>(private val inject: () -> Type) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>) : Type = inject()
}

fun EventsHandler.textBlockEvents(
    engine: () -> Engine,
    block: () -> DesignBlock,
    fontFamilyMap: () -> Map<String, FontFamilyData>
) {
    val engine by Inject(engine)
    val block by Inject(block)
    val fontFamilyMap by Inject(fontFamilyMap)
    // Event handling logic here
}

Step 7: Register Multiple Handlers Respecting SRP

By composing several small handler functions— blockEvents, cropEvents, textBlockEvents —inside a single EventsHandler instance, we keep each responsibility isolated while sharing the same dispatch mechanism:

private val eventHandler = EventsHandler {
    cropEvents(engine = ::engine, block = ::getBlockForEvents)
    blockEvents(engine = ::engine, block = ::getBlockForEvents)
    textBlockEvents(
        engine = ::engine,
        block = ::getBlockForEvents,
        fontFamilyMap = { checkNotNull(assetsRepo.fontFamilies.value).getOrThrow() }
    )
    // ...
}

fun EventsHandler.blockEvents(engine: () -> Engine, block: () -> DesignBlock) {
    val engine: Engine by Inject(engine)
    val block: DesignBlock by Inject(block)
    register<BlockEvent.OnDelete> { engine.delete(block) }
    register<BlockEvent.OnBackward> { engine.sendBackward(block) }
    // ... other registrations ...
}

Finally, the public onEvent simply forwards the event to the composed handler, preserving the original API while benefiting from the refactored architecture.

Conclusion

By systematically applying HashMap‑based dispatch, payload objects, infix and inline functions, and delegated property injection, the Kotlin event‑handling code achieves dramatically better performance (constant‑time lookup), clearer syntax, and strict adherence to the single‑responsibility principle, empowering developers to write elegant, maintainable code.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

performanceKotlinHashMapdependency-injectionrefactoringInfix Functionsinline functions
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.