smallufo
08/08/2021, 8:35 PMclass Config(var degree: Double = 315.0) {
init {
require(degree > 180 && degree < 360)
{ "degree should between 180 and 360" }
}
}
And if we initialize Config
directly , it will check the value as expected :
@Test
fun normalTest() {
try {
Config(90.0)
fail()
} catch (e: Exception) {
assertTrue(true)
}
}
But if there is a function accepting the Config
as functional object :
private fun calculate(block: Config.() -> Unit = {}) {
val cfg = Config().apply(block)
println("do something... with degree = ${cfg.degree}")
}
@Test
fun testCalculate() {
try {
calculate {
degree = 90.0
}
fail("It SHOULD fail")
} catch (e: Exception) {
assertTrue(true)
}
}
It will skip the init
check block .
How to solve this problem ? Thanks.
I could only think of this solution :
class Config(var degree: Double = 315.0) {
init {
check()
}
fun check() {
require(degree > 180 && degree < 360)
{ "degree should between 180 and 360" }
}
}
and …
private fun calculate(block: Config.() -> Unit = {}) {
val cfg = Config().apply(block).also { it.check() }
println("do something... with degree = ${cfg.degree}")
}
But I think it is so ugly … Is there any better way ? Thanks.Ayfri
08/08/2021, 8:59 PMdegree
property, in which you put the require
in its init
function like you didRob Elliot
08/08/2021, 9:03 PMinit
is called when the object is constructed. When you call Config().apply(block)
, the first thing that happens is Config()
, with your default value for degree
of 315.0
. So the init
is called, and passes, because 315 is between 180 and 360.
You then set degree
to 90
- but you have no guard on changing degree
.
Do you really want Config
to be mutable? If so, and you want to keep the invariant that degree > 180 && degree < 360
, you need to add it to `degree`’s setter.smallufo
08/08/2021, 9:07 PMConfig
class.
to @Rob Elliot: Config
needs to be mutable , so that client of the function may call calculate
like this
calculate {
degree = 90.0
}
Rob Elliot
08/08/2021, 9:08 PMConfig(degree = 90.0)
and make degree
a val
?smallufo
08/08/2021, 9:11 PMktor
's config (ex : https://ktor.io/docs/http-client-engines.html#java )
such as :
calculate (obj1 , obj2 ) {
degree = 90.0
angle {
value = 60.0
}
}
Rob Elliot
08/08/2021, 9:12 PMclass Config(
degree: Double = 315.0
) {
var degree: Double = degree
set(value) {
require(value > 180 && value < 360)
field = value
}
init {
this.degree = degree
}
}
smallufo
08/08/2021, 9:15 PMRob Elliot
08/08/2021, 9:16 PMapply
is an extension function you call after the object has been constructed. Basically you pass the constructed Config instance as an argument to apply
, so it absolutely has to already have been constructed.Rob Elliot
08/08/2021, 9:19 PMsmallufo
08/08/2021, 9:20 PMRob Elliot
08/08/2021, 9:21 PMConfig().apply { degree = 90 }
is exactly the same as:
val config = Config()
config.degree = 90
Ayfri
08/08/2021, 9:24 PMapply
function ?ephemient
08/08/2021, 10:02 PMclass ConfigBuilder {
var degree: Double = 315.0
}
class Config(val degree: Double)
fun Config(block: ConfigBuilder.() -> Unit): Config {
val builder = ConfigBuilder()
builder.block()
return Config(builder.degrees)
}
then you can
Config { degree = 90.0 }
and a check in Config.init will happen after the block runssmallufo
08/08/2021, 10:39 PM