Невозможно вызвать функцию из блока init из-за свойства val

Я хотел бы инициализировать свойства моего класса. Поскольку я интенсивно использую функциональные элементы Kotlin, я хотел бы поместить эти инициализации в хорошо именованные функции, чтобы повысить удобочитаемость моего кода. Проблема в том, что я не могу назначить свойство val, если код находится не в блоке init, а в функции, которая вызывается из блока init.

Можно ли разобрать инициализацию класса для разных функций, если свойства являются vals?

Вот код:

    val socket: DatagramSocket = DatagramSocket()
    val data: ByteArray = "Cassiopeiae server discovery packet".toByteArray()
    val broadcastAddresses: List<InetAddress>

    init {
        socket.broadcast = true
        val interfaceAddresses = ArrayList<InterfaceAddress>()
        collectValidNetworkInterfaces(interfaceAddresses)
        collectBroadcastAddresses(interfaceAddresses)
    }

    private fun collectValidNetworkInterfaces(interfaceAddresses: ArrayList<InterfaceAddress>) {
        NetworkInterface.getNetworkInterfaces().toList()
                .filter { validInterface(it) }
                .forEach { nInterface -> nInterface.interfaceAddresses.toCollection(interfaceAddresses) }
    }

    private fun collectBroadcastAddresses(interfaceAddresses: ArrayList<InterfaceAddress>) {
        broadcastAddresses = interfaceAddresses
                .filter { address -> address.broadcast != null }
                .map { it.broadcast }
    }

Конечно, это не компиляция, потому что функция collectBroadcastAddresses пытается переназначить значение broadcastAddresses. Хотя я не хочу помещать код этой функции в блок init, потому что неясно, что делает код, и имя функции говорит об этом очень хорошо.

Что я могу сделать в таких случаях? Я хотел бы сохранить мой код в чистоте, это самый важный момент!

1 ответ

Решение

Одним из способов решения этой проблемы является использование чистых функций для инициализации полей:

class Operation {
    val socket = DatagramSocket().apply { broadcast = true }
    val data: ByteArray = "Cassiopeiae server discovery packet".toByteArray()
    val broadcastAddresses = collectBroadcastAddresses(collectValidNetworkInterfaces()) 

    private fun collectValidNetworkInterfaces() =
        NetworkInterface.getNetworkInterfaces().toList()
            .filter { validInterface(it) }
            .flatMap { nInterface -> nInterface.interfaceAddresses }

    private fun validInterface(it: NetworkInterface?) = true

    private fun collectBroadcastAddresses(interfaceAddresses: List<InterfaceAddress>) {
        interfaceAddresses
            .filter { address -> address.broadcast != null }
            .map { it.broadcast }
    }
}

Обратите внимание, как socket инициализация поля использует apply расширение.

Я часто нахожу полезным извлекать процедуры манипуляции коллекциями в методы расширения:

class Operation {
    val socket = DatagramSocket().apply { broadcast = true }
    val data: ByteArray = "Cassiopeiae server discovery packet".toByteArray()
    val broadcastAddresses = NetworkInterface.getNetworkInterfaces()
        .collectValidNetworkInterfaces { validInterface(it) }
        .collectBroadcastAddresses()

    private fun validInterface(it: NetworkInterface?) = true
}

fun Iterable<InterfaceAddress>.collectBroadcastAddresses(): List<InetAddress> =
    filter { address -> address.broadcast != null }.map { it.broadcast }

fun Enumeration<NetworkInterface>.collectValidNetworkInterfaces(isValid: (NetworkInterface) -> Boolean = { true }) =
    toList()
        .filter { isValid(it) }
        .flatMap { nInterface -> nInterface.interfaceAddresses }
Другие вопросы по тегам