I have a problem with very long init block in my class, so i decided to split it into functions.
How it looks now:
class Foo {
val property1: Int
val property2: String
init {
property1 = 1
property2 = "two"
}
}
After changes it should look like this:
class Foo {
val property1: Int
val property2: String
init {
initProperty1()
initProperty2()
}
fun initProperty1() {
property1 = 1
}
fun initProperty2() {
property2 = "two"
}
}
But there is no way to do it.
Val cannot be reassigned
Property must be initialized or be abstract
So what about adding this feature? It can make code much cleaner.
The thing is that long initialization logic in kotlin is a code smell anyway. The initialization order is the major pitfall anyway. If you insist on using initialization logic and want to use separate functions for that, just write val property1: Int = initProperty1().
Ok my example wasnt clear enough. I wanted to initialize not only one property in function. I’m getting some resources from file and these resources contain information to initialize couple properties.
Here’s how it’s supposed to work in my code.
class MyClass {
// foo.json
val foo1: Int
val foo2: Int
// bar.json
val bar: String
init {
initFoo()
}
fun initFoo() {
// parse json
foo1 = ; // initialization
foo1 = ; // initialization
}
fun initBar() {
// parse json
bar = ; // initialization
}
}
While writing it i’ve thought about something like this:
class MyClass {
companion object {
// foo.json
val foo1: Int = fooDict["foo1"]
val foo2: Int = fooDict["foo2"]
// bar.json
val bar: String = barDict["bar"]
init {
val fooDict: HashMap<String, Int> = parseJson("foo.json")
val barDict: HashMap<String, Int> = parseJson("bar.json")
}
}
}
So is there a way to initialize temporary property that will be deleted after initializing?
What I tried to tell you is that doing heavy logic and parsing json in the initializer block is a bad practice in kotlin anyway. The idiomatic way is to create a private constructor that accepts the processed values and move the initialization logic to a factory function.
So how do i initialize static properties then? Constructors are not allowed for companion objects. How to deal with properties described in JSONs then? Couple links would be nice, thanks)
Yes, factory methods in the companion object is a common pattern in Kotlin.
You can even make them look like a constructor, callable with Classname(…), by defining them as operator fun invoke(…).
That’s not always appropriate. In Java, factory methods are recommended where a method name would add useful extra information, and to avoid having multiple constructors that could get confused (especially if they have the same number of parameters with similar types) — both of which are reasons for explicitly-named factory methods. But if there’s no risk of confusion, and no extra information you can add, then making them look like constructors can give the calling code one less thing to worry about.
However you name them, factory methods are a powerful tool and well worth considering. (In fact, they’re the very first item in all 3 editions of Effective Java!) Other advantages that apply whatever the name are that you can return a subclass and/or a cached object; and they don’t have the restriction on the amount of processing you can do before calling another constructor or a superclass constructor.
Factory methods have downsides, too, of course — the main one being making it awkward to subclass. But if you have control over the superclass, you can usually work around that.
Your comment about subclassing is not quite relevant here since we are talking about secondary constructors. If you want to subclass something, you just make the constructor public or protected. In fact, inheritance is exactly the case, where factory methods are better than constructors since you can avoid the confusion about which descendant constructor calls which parent constructor.
And if you want a subclass with a constructor or factory method that works like the superclass’s factory method?
The standard approach (having your constructor call a superclass constructor) doesn’t work for factory methods; if you call the superclass’s one, you normally get a superclass instance back.
So you either have to duplicate the entire factory method in your subclass, or you need to modify the superclass somehow so it knows about your subclass and can return an instance of that under the appropriate conditions. And neither cut-and-paste coding nor tight coupling are things to encourage.
However… people shouldn’t let that put them off factory methods, which are otherwise a great idea!
I kind of agree. I would often want to have a secondary constructor that would call this factory (which is either a free function or companion object function) and forward that to another constructor. Of course it depends on whether the handling of parsing is logically part of the class. If not, there may need to be another class or function that is responsible. For example for loading configuration from a file, if it could be multiple formats, the loading could be separated from the configuration class itself. With the caveat that every added class adds complexity and as long as in all cases you have access to the source (this is not in a library) it is normally better not to add the complexity until you need to (as you can fix at any point you want)
My application has to parse JSONs to work, it made once on startup. I’m making discord bot with spring and MyClass in my examples is basically Bot class, whereas JSONs contain information about token and other stuff for setting up my bot. I wanted to call all this parameters statically without depending on a bean, so i decided to create static class with properties inside Bot class.
This is how it looks now:
@Component
class Bot
@Autowired constructor(
messageListener: MessageListener,
) {
init {
jda.listener(messageListener::onMessageReceived)
}
companion object Properties {
// bot.json
val token: String
val name: String
init {
val gson = Gson()
val botProperties: HashMap<String, String> =
gson.fromJsonAsType(createJsonString("bot.json"))
try {
token = botProperties["token"]!!
name = botProperties["name"]!!
} catch (exception: NullPointerException) {
throw PropertyNotFoundException("Please provide correct properties.")
}
}
}
}
Why don’t you use the Spring config facilities and create a config class that Spring uses to create a config object that you can then inject into your Bot class?
In general, I’d keep things like Json parsing out of the classes that are using the parsing result. You can check your architecture, if you think about parts of your application that would change if you would switch to XML. The Bot class shouldn’t change in this case.