Dalinar
09/14/2017, 6:32 AMpostInit { }
or ready { }
block that is called in the parent after all subclasses are fully constructed.
Right now I'm implementing an abstract fun() that the child must override (to ensure that all subclasses always call the parent initializer method) (at the bottom of the child class) to call an initalize() fun in the parent. I'm wondering if there is a better way?
Because the code that I would normally put in the init { }
block needs to reference an abstract val that (implemented in the child) references a constructor property of that childCzar
09/14/2017, 7:33 AMDalinar
09/14/2017, 7:55 AMkz
09/14/2017, 3:13 PMMarc Knaup
04/25/2021, 5:07 PMpostInit
in my mind.
Is there anything in Kotlin by now that gets executed in a superclass once the subclass is fully initialized?
That would be really useful.
In my case I create anonymous objects that define data with DSL (val foo by dsl { … }
). dsl
is function of the superclass.
After all data is defined the superclass must further combine/process it. It can only do so once it knows that all data is defined.
At the moment I’d basically have to add the following at the bottom of all 100+ subclasses:
init { process() }
ephemient
04/25/2021, 5:14 PMephemient
04/25/2021, 5:16 PMMarc Knaup
04/25/2021, 5:16 PM@KotlinOnly
?
I don’t need JVM compatibility anyway.Youssef Shoaib [MOD]
04/25/2021, 5:32 PM@JvmSynthetic
is basically @KotlinOnly
but only on the JVMMarc Knaup
04/25/2021, 5:33 PMDominaezzz
04/25/2021, 5:36 PMfreeze()
the class after subclasses are done initialising fields.Youssef Shoaib [MOD]
04/25/2021, 5:39 PMDominaezzz
04/25/2021, 5:44 PMMarc Knaup
04/25/2021, 5:49 PMRegardless though, I agree that you need a redesign. Maybe use composition over inheritance.Agree with who? And I don’t think you have enough context for that. I haven’t found any way to change the design without increasing complexity and decreasing usability. Kotlin isn’t very good for static type-safe DSL yet. That requires anonymous objects. The only other way is what I have as a temporary work: All use-sites (anonymous objects) must manually complete the initialization. That effectively moves an implementation detail down in hierarchy.
ephemient
04/25/2021, 5:59 PMMarc Knaup
04/25/2021, 6:11 PMval styles = object : StyleSheet {
val button by style { … } /* : String */
val input by style { … } /* : String */
}
val globalStyles = object : StyleSheet {
val primaryAccess by style { … } /* : String */
}
Consumers of data that was defined by such static DSL can simply use the properties:
button(styles.button, globalStyles.primaryAccent) { … }
For that to work the StyleSheet
base class will need to register all styles it defines, merged into a single block of CSS text.
With regular DSL that’s easy, because the DSL controls entry and exit points:
fun styleSheet(init: StyleSheet.() -> Unit) {
// before init
init()
// after init
}
styleSheet {
val button by style { … }
}
So when styleSheet()
is done with init()
it can complete the initialization.
That is not currently possible if you need the variables accessible outside of the DSL block as properties.
So the initial solution above remains. Any other solution would make the DSL significantly more verbose and repetitive.
The only difference between the two is that the former approach (with anon object) cannot do anything at the exit point of the initialization.ephemient
04/25/2021, 6:14 PMstyleSheet()
?Dominaezzz
04/25/2021, 6:14 PMThat is not currently possible if you need the variables accessible outside of the DSL block as properties.
ephemient
04/25/2021, 6:15 PMval (styles, button, input) = styleSheet {
exportStyle { /* button */ }
exportStyle { /* input */ }
}
Marc Knaup
04/25/2021, 6:16 PMkotlin-styled
I’m just writing my own library on the same basis because it makes most sense.Marc Knaup
04/25/2021, 6:19 PMMarc Knaup
04/25/2021, 6:20 PMMarc Knaup
04/25/2021, 6:20 PMephemient
04/25/2021, 6:20 PMephemient
04/25/2021, 6:21 PMephemient
04/25/2021, 6:22 PMstyles["button"]
as well, which of course has different tradeoffsMarc Knaup
04/25/2021, 6:23 PMkotlin-styled
and kotlin-css
provide.
Both libraries have some issues though so I’m building my own. kotlin-styled
DSL didn’t solve my particular issue and they have to work around that too in quite inefficient ways.Dominaezzz
04/25/2021, 6:25 PMMarc Knaup
04/25/2021, 6:26 PMMarc Knaup
04/25/2021, 6:27 PMMarc Knaup
04/25/2021, 6:30 PMNir
04/26/2021, 12:17 AMNir
04/26/2021, 12:17 AMval styles = object : StyleSheet {
val button by style { … } /* : String */
val input by style { … } /* : String */
}
Nir
04/26/2021, 12:18 AMval styles = makeStyle {
object : StyleSheet {
...
}
}
Nir
04/26/2021, 12:19 AMmakeStyle
execute some code after initializing the objectYoussef Shoaib [MOD]
04/26/2021, 12:51 AMmakeStyle
provides you with a StyleScope
that you're forced to pass to the constructor like this:
abstract class StyleSheet(protected val createdInScope: StyleScope) {
init {
createdInScope.registerForPostInit(this)
}
// other code
}
class StyleScope internal constructor() {
internal val sheets = mutableListOf<StyleSheet>()
fun registerForPostInit(sheet: StyleSheet) {
if(isClosed) throw IllegalStateException()
sheets.add(sheet)
}
// Safety feature just to ensure that no one will ever use this scope out-of-context
internal var isClosed = false
}
inline fun makeStyle(block: StyleScope.() -> Unit) {
val scope = StyleScope()
scope.block()
scope.isClosed = true
for(sheet in scope.sheets) {
// run post-init here
}
}
// Then in user code
val styles = makeStyle {
object : StyleSheet(this@makeStyle) {
}
}
Youssef Shoaib [MOD]
04/26/2021, 12:54 AMNir
04/26/2021, 12:58 AMStyleSheet
is private to the object passed as this
inside makeStyle
Nir
04/26/2021, 12:58 AMmakeStyle
, and therefore no way to avoid the post-initNir
04/26/2021, 12:58 AMYoussef Shoaib [MOD]
04/26/2021, 12:59 AMYoussef Shoaib [MOD]
04/26/2021, 12:59 AMNir
04/26/2021, 12:59 AMNir
04/26/2021, 12:59 AMNir
04/26/2021, 1:00 AMobject : StyleSheet
will ultimately only work inside the lambda passed to makeStyle
Nir
04/26/2021, 1:04 AMMarc Knaup
04/26/2021, 3:35 PMStyleSheet
class. That class can then either be removed or serve as a marker (e.g. an external interface
in KJS).Marc Knaup
04/26/2021, 3:37 PMNir
04/26/2021, 3:53 PMNir
04/26/2021, 3:54 PMauto
in C++), it effectively supports it because any function can be implemented using = run { .... }
which does return type deductionMarc Knaup
04/26/2021, 3:57 PMobject : StyleSheet
to StyleSheet
. The UI even indicates it like that.
But because it maintains the anonymous type that’s quite useful.Nir
04/26/2021, 4:01 PMNir
04/26/2021, 4:01 PM