gstackoverflow
gstackoverflow

Reputation: 36996

Is there way to have multi resource "try with resources" in Kotlin?

I have java function(taken from here):

/**
 * Checks to see if a specific port is available.
 *
 * @param port the port to check for availability
 */
public static boolean available(int port) {
    if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {
        throw new IllegalArgumentException("Invalid start port: " + port);
    }

    ServerSocket ss = null;
    DatagramSocket ds = null;
    try {
        ss = new ServerSocket(port);
        ss.setReuseAddress(true);
        ds = new DatagramSocket(port);
        ds.setReuseAddress(true);
        return true;
    } catch (IOException e) {
    } finally {
        if (ds != null) {
            ds.close();
        }

        if (ss != null) {
            try {
                ss.close();
            } catch (IOException e) {
                /* should not be thrown */
            }
        }
    }

    return false;
}

I want to rewrite it in Kotlin without ugly constructions.

So I've found this topic:

Try-with-resources in Kotlin

As far I understood I have to create object first and inside of use lambda I have to to call method which could throw Exception but in case of

ServerSocket(port)
DatagramSocket(port)

Exception will be thrown in constructor. So looks like it doesn't fit my needs.

So how can I rewrite this code in Kotlin ?

Upvotes: 0

Views: 250

Answers (3)

Slaw
Slaw

Reputation: 46255

Exception will be thrown in constructor. So looks like it doesn't fit my needs.

If the constructor throws an exception, then there is no object to close. This applies to both Java and Kotlin. You even see the Java code handling this by checking if ss and ds are null.


That Java code you found looks like it was written before try-with-resources statements were added to the language. In modern Java, it would look more like this:

public static boolean available(int port) {
    if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {
        throw new IllegalArgumentException("Invalid start port: " + port);
    }

    try (var ss = new ServerSocket(port)) {
        ss.setReuseAddress(true);
        try (var ds = new DatagramSocket(port)) {
            ds.setReuseAddress(true);
            return true;
        }
    } catch (IOException e) {
        return false;
    } 
}

Translating that to Kotlin could look something like this:

fun available(port: Int): Boolean {
    require(port in MIN_PORT_NUMBER..MAX_PORT_NUMBER) { "Invalid start port: $port" }

    return try {
        ServerSocket(port).use { ss -> 
            ss.reuseAddress = true
            DatagramSocket(port).use { ds ->
                ds.reuseAddress = true
                true
            }
        }
    } catch (_: IOException) {
        false
    }
}

The use function will handle calling close for you. From the documentation:

inline fun <T : Closeable?, R> T.use(block: (T) -> R): R

Executes the given block function on this resource and then closes it down correctly whether an exception is thrown or not [emphasis added].

Upvotes: 2

Per Huss
Per Huss

Reputation: 5105

If the exception is thrown in the constructor, you will have no reference to close... I think this could be written in idiomatic kotlin like this:

return runCatching {
    ServerSocket(port).use { ss ->
        ss.reuseAddress = true
        DatagramSocket(port).use { ds ->
            ds.reuseAddress = true
        }
    }
}.isSuccess

The use function will close the resource as specified in the use documentation.

Upvotes: 1

waseem akram
waseem akram

Reputation: 101

that code in kotlin will look like this

    import java.io.IOException
    import java.net.DatagramSocket
    import java.net.ServerSocket
    
    fun available(port: Int): Boolean {
        if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {
            throw IllegalArgumentException("Invalid start port: $port")
        }
    
        return try {
            createAndConfigureServerSocket(port)
            createAndConfigureDatagramSocket(port)
            true
        } catch (e: IOException) {
            false
        }
    }
    
    private fun createAndConfigureServerSocket(port: Int) {
        ServerSocket(port).use { ss ->
            ss.reuseAddress = true
            ss.close()
        }
    }
    
    private fun createAndConfigureDatagramSocket(port: Int) {
        DatagramSocket(port).use { ds ->
            ds.reuseAddress = true
        }
    }
    
    const val MIN_PORT_NUMBER = 0
    const val MAX_PORT_NUMBER = 65535

Upvotes: 0

Related Questions