When creating a secondary constructor, first I have to call the primary constructor and only then I’m allowed to code, here’s an example when it’s interrupting:
// That's what I want to write!
class SomeViewHolder(val view: View, val firstTextView: TextView, val secondTextView: TextView){
constructor(view: View){
var textView1 = view.findViewById(R.id.some_id_1)
var textView2 = view.findViewById(R.id.some_id_2)
// maybe here I would modify the textViews
// and then initializing the class
this(view, textView1, textView2)
}
}
// That's what I have to write
class SomeViewHolderIHaveToWrite(val view: View){
val firstTextView: TextView
val secondTextView: TextView
init {
firstTextView = view.findViewById(R.id.some_id_1)
secondTextView = view.findViewById(R.id.some_id_2)
}
}
// That's also an example of what I have to write- with more complex parameters it will be impossible to construct it
class SomeViewHolderIHaveToWrite2(val view: View, val firstTextView: TextView, val secondTextView: TextView){
constructor(view: View): this(view, view.findViewById(R.id.some_id_1), view.findViewById(R.id.some_id_2))
}
class SomeViewHolderIHaveToWrite(val view: View){
val firstTextView = view.findViewById(R.id.some_id_1) as TextView
val secondTextView = view.findViewById(R.id.some_id_2) as TextView
}
if you prefer to have optional view parameters:
class SomeViewHolderIHaveToWrite(
val view: View,
val firstTextView: TextView = view.findViewById(R.id.some_id_1) as TextView,
val secondTextView: TextView = view.findViewById(R.id.some_id_2) as TextView
)
not an android expert, but why not just use one of this:
I would like two kinds of constructors- the first is primary- that will be at the class header,
It will also define my variables.
The second constructor will get a single View and will initialize the other variables the way he want.
Eliminate this code because the initialization could take more than one line of code
class SomeViewHolderIHaveToWrite(
val view: View,
val firstTextView: TextView = view.findViewById(R.id.some_id_1) as TextView,
val secondTextView: TextView = view.findViewById(R.id.some_id_2) as TextView
)
about the frameworks- it’s a simple example, so it’s irrelevant.
The thing is I want to declare my variables in the primary constructor at the head of the class and still be able to use them as if I would have written them inside the class. for example:
that:
class SomeViewHolderIHaveToWrite(val view: View){
val firstTextView: TextView
val secondTextView: TextView
}
should be equivalent with all means to that:
class SomeViewHolder(val view: View, val firstTextView: TextView, val secondTextView: TextView)
most obvious one, decompose complex initialization statements into a simple pure functions:
private fun foo(view: View): TextView = TODO("complex initialization here")
private fun bar(view: View): TextView = TODO("complex initialization here")
class SomeViewHolderIHaveToWrite(
val view: View,
val firstTextView: TextView = foo(view),
val secondTextView: TextView = bar(view)
)
nullable arguments:
class SomeViewHolderIHaveToWrite(
val view: View,
firstTextView: TextView? = null,
secondTextView: TextView? = null
){
val firstTextView: TextView = firstTextView ?: run {
TODO("complex initialization here")
}
val secondTextView: TextView = secondTextView ?: run {
TODO("complex initialization here")
}
}
fabric methods:
// hide primary constructor for uniformity/consistency
class SomeViewHolderIHaveToWrite private constructor(
val view: View,
val firstTextView: TextView,
val secondTextView: TextView
){
companion object {
fun create(view: View): SomeViewHolderIHaveToWrite {
TODO("complex initialization here")
}
fun create(view: View, firstTextView: TextView, secondTextView: TextView) =
SomeViewHolderIHaveToWrite(view, firstTextView, secondTextView)
}
}
“fake” constructors:
class SomeViewHolderIHaveToWrite(
val view: View,
val firstTextView: TextView,
val secondTextView: TextView
)
fun SomeViewHolderIHaveToWrite(view: View): SomeViewHolderIHaveToWrite {
TODO("complex initialization here")
}
Thank you for your answers, cool workarounds, at the end there’s always a workaround, just thought to mention the need for a workaround around this problem.
The language let you declare your variables at the class header along with the primary constructor, so if there’s a way to declare it on there, why are they limitations?
any valid kotlin expression can act as field/parameter initializer. So you can use run or let, but its a bit ugly:
class SomeViewHolderIHaveToWrite(
val view: View,
val firstTextView: TextView = run {
TODO("multi-line")
TODO("initialization")
}
val secondTextView: TextView = run {
TODO("multi-line")
TODO("initialization")
}
)
The design of secondary constructors is influenced by a restriction we find in Java. There, the first statement must call another constructor. That is why Kotlin team decided to use : this() syntax. Yes, they might have been able to generate a factory method instead. Various considerations are at play here though. Happy coding.
Nah. While Java requires it, the JVM doesn’t. It can’t as you can initialise with any expression (also in Java). The reason is more like in Java. It is invalid to operate on partially constructed values. If you do something before invoking the parent this means that the parent is not yet initialised at that point. Any access of the object at that time is (likely) to be invalid. The same holds with primary/secondary constructors. The reason it is designed as is (and why Java does it) is because it is a large surface for programmer error.