Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot call a function from init block because of val property

I'd like to initialize my class's properties. Because I'm using heavily the functional elements of Kotlin, I'd like to put these initializations to well named functions, to increase readability of my code. The problem is that I cannot assign a val property, if the code is not in the init block, but in function which is called from the init block.

Is it possible to take apart initialization of a class, to different functions, if the properties are vals?

Here is the code:

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

Of course it's not compiling, because collectBroadcastAddresses function tries to reassign the broadcastAddresses val. Although I don't want to put the code of this function to the init block, because it's not obvious what the code is doing, and the function name tells it very nicely.

What can I do in such cases? I'd like to keep my code clean, this is the most important point!

like image 648
LordScone Avatar asked Dec 13 '25 05:12

LordScone


1 Answers

One way of approaching the problem is to use pure functions to initialize fields:

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

Notice how the socket field initialization uses apply extension.

I often find it useful to extract collection manipulation routines into extension methods:

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 }
like image 71
miensol Avatar answered Dec 16 '25 11:12

miensol



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!