Reputation: 36996
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:
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
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
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
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