I can set class `val` in `init` or in a constructo...
# announcements
t
I can set class
val
in
init
or in a constructor, but not both? What's the reasoning behind this code not compiling? https://pl.kotl.in/lVxY8idN4
n
I'm not sure. the relevant part of the spec is this though
the last two bullets I think are the problem
init blocks run before the constructors
t
hmmm... that would explain it. Although I swear I saw some kotlin docs someplace that said exactly the opposite. That init {} always ran last. Lemmie see if I can find where I read that.
yeah. so this says init runs last.... althought it wasn't written by jetbrains and it's 5 years old : https://medium.com/keepsafe-engineering/an-in-depth-look-at-kotlins-initializers-a0420fcbf546
n
I'd defer to the specification on this one 🙂
s
init definitely runs last, you can test it yourself pretty trivially w/ some print statements
also I know this is just a playground example but this doesn’t really look like how you’d want to write class constructors in Kotlin
another thing you might keep in mind is that you can have as many
init
blocks as you want in a class and that they’ll run in the order they’re declared (but all before the secondary constructors)
n
this may defeat the point of your example but this is equivalent https://pl.kotl.in/hqjHo1jQ2
t
unsurprisingly, it looks like the spec is correct. This doc explains it well. Basicaly w/o a primary constructor (which this example does not have) init{} is treated like the primary constructor so it runs first, regardless of the order in the file. https://kotlinlang.org/docs/classes.html#secondary-constructors
I have a class which creates and holds a bunch of services in vals. For some testing I'm doing I want to create a new one of these, but I want to pass in a few specific services I made in another way. Currenly most of the services are created in the init {} block... so I was trying to see if I could use constructors to make pass a few in.. but it looks like that's a dead end. Or a copy a bunch of code into various constructors (cuz you can't call a function to init a val from init {} or a constructor)
it's not just assignment... creating these things is work.. reading config etc. so a primary construct that @nanodeath showed won't work.
I guess I could make them latent vars... I just hate doing that.
also if I change
three = one * two
to
three = 3
it compiles. It's the reference to the uninitialized vals that's the issue.
n
maybe do that work in a factory method instead of the constructor?
s
I feel like constructors and init blocks in Kotlin are a bit misunderstood. Folks tend to use them like Java constructors where they pretty much have free reign to do whatever they want, but they’re not really intended to be used in the same way. The primary constructor is designed to define the dependencies of a class and really not much else at all. In the presence of a primary constructor, init blocks and secondary constructors are largely used to perform a bit more logic after allocation and assignment. If you want to provide different ways to construct a class by changing its dependencies, use factory functions to perform whatever logic you need to do before building a new object. They also have the advantage of being able to be named descriptive things
but if you really don’t like the named function approach, you can emulate the constructor syntax by defining your factory function as an
operator fun invoke
in the class’s
companion object
t
yeah. I can do it in a factory. it's a startup only thing... so it doesn't matter that much.
oh! no I can't. Some of the things this class creates need a instance of this class itself... turns out you can pass
this
from
init{}
which... admittedly is bad. I have shitty dependency startup issues which I punted on... I guess I need to actually think about it and try to untangle them. drat.
c
This works:
Copy code
class Foo(val a: Int) {
  private val b: Boolean
Copy code
init {
    b = a == 0
  }
}
An explicitely-declared constructor always runs last. As a coding style thing, it's really rare to use secondary constructors, and when you do they should probably call the main one.
@TwoClocks you can write something like this:
Copy code
class Foo(
  val service1 = initService1(),
  val service2 = initService2(),
  ...
)
Then, the caller can substitute whichever they want with a custom one, and you don't even need secondary constructors.
t
@CLOVIS close, but I actually need
class Foo {
val service1 = initService1( this )
...
Which makes things a little more complex.
and sometimes I want to create a
service
and other times I want to supply it to Foo in it's constructor.
but this is only cuz I have a bunch of services that depend on each other... the logger wants the networkEventLoop and visa-versa. I just need to spend the time and go clean all that crap up. I was just writing a test case and wanted to see if I could hack it up w/o doing the hard work... I can't.