Reputation: 5
Im trying to overload a constructor of a class, so that it accepts Strings as well as Ints.
class Load (val duration: Int = 0,val minrep: Int = 0,val maxrep: Int = 0): Serializable{
constructor(duration: String = "0",minrep: String = "0",maxrep: String = "0")
: this(duration.toInt(),minrep.toInt(),maxrep.toInt())
The Problem is since I am forced to call the superconstructor immediatly, I cannot check for "" in the variables which is a possible state which then crashes the app in to .toInt().
So what I would want to do is something like this:
constructor(duration: String = "0",minrep: String = "0",maxrep: String = "0") {
if(duration == "")
duration = "0"
...
this(duration.toInt(),minrep.toInt(),maxrep.toInt())
}
But How?
Upvotes: 0
Views: 1285
Reputation: 18577
There's a quick-and-dirty method; you just need to know that if
is an expression in Kotlin:
class Load(val duration: Int = 0, val minrep: Int = 0, val maxrep: Int = 0): Serializable {
constructor(duration: String = "0", minrep: String = "0", maxrep: String = "0")
: this(if (duration == "") 0 else duration.toInt(), minrep.toInt(), maxrep.toInt())
}
However, that gets unwieldy very quickly, and there's a limit to the sort of processing you can do that way. (As another answer shows, you can call helper methods, but it's still awkward.)
So a more general solution is to use a factory method: one which does whatever processing it needs to, and then calls the constructor. In Java this would be a static method; the Kotlin equivalent is a method on the class's companion object
:
class Load(val duration: Int = 0, val minrep: Int = 0, val maxrep: Int = 0): Serializable {
companion object {
operator fun invoke(duration: String = "0", minrep: String = "0", maxrep: String = "0"): Load {
val dur = if (duration == "") 0 else duration.toInt()
// ...
return Load(dur, minrep.toInt(), maxrep.toInt())
}
}
}
The magic here is that by making it operator fun invoke()
, you can then call it as Load(…)
, so it looks just like calling a normal constructor!
(This is only appropriate if it's obvious what it does, just from the parameter types alone. If it's not obvious, or if you need two factory methods with the same parameter types, a regular named method is better.)
Factory methods give you a lot more control over how your objects get created: they let you do arbitrary amounts of processing before calling the constructor; they can return a cached object instead of creating a new instance each time; and they can return a subclass if that's appropriate. (However, one drawback is that they don't work well with inheritance.)
Upvotes: 1
Reputation: 7882
Extract additional data transformation logic to some function and call it instead of simple .toInt()
:
class Load(val duration: Int = 0, val minrep: Int = 0, val maxrep: Int = 0) {
companion object {
//`String` will have this extension function only in the scope of `Load` class
private fun String.toIntOrZero() = toIntOrNull() ?: 0
}
constructor(duration: String = "0", minrep: String = "0", maxrep: String = "0")
: this(duration.toIntOrZero(), minrep.toIntOrZero(), maxrep.toIntOrZero())
}
Note that in this implementation all other strings unparseable as Int
would be as well fallbacked to 0
Upvotes: 2
Reputation: 5
I solved it like this for now, but coming from python this feels unsexy and bloated, there must be a better way:
class Load (duration: String = "0",minrep: String = "0",maxrep: String = "0"): Serializable{
var m_duration : Int = 0
var m_maxrep : Int = 0
var m_minrep : Int = 0
init{
m_duration = if(duration=="") 0 else duration.toInt()
m_maxrep = if(maxrep=="") 0 else maxrep.toInt()
m_minrep = if(minrep=="") 0 else minrep.toInt()
}
constructor(duration: Int = 0,minrep: Int = 0,maxrep: Int = 0)
: this(duration.toString(),minrep.toString(),maxrep.toString())
Upvotes: 0