Actually, on the topic of constructors: is there a...
# compiler
c
Actually, on the topic of constructors: is there anything in the JVM that requires that
<init>()V
is the initializer? Besides the fact that it's the code generated when you do a constructor call, I mean. There's some really neat stuff we could do if we could blend constructor and factory methods in the language, like letting failed initializers return null instead of throwing an exception as control flow.
e
y
Well, the language blends those two pretty closely already. Constructors are effectively only a concept for the class writer, not the class user (except when interoping with other languages). Hence, factory methods are simply more powerful and don't have any real disadvantages.
c
Yeah, I've never been a big fan of constructors as a hardcoded type of function. There's no functional difference between a constructor and an encapsulated factory method, but constructors have so many weird limitations that don't make sense. It would be nice if Kotlin let you define factory methods inside the class instead of as
Companion.invoke
methods, it would be a lot less verbose. Of course that would also be resolved if
static
ever gets added.
y
The plan seems to be that companion invoke is the idiomatic way. The new
companion
proposal specifically only allows 2 operators on this
companion
block:
invoke
and
of
(for collection literals)
So under that proposal, this is considered idiomatic:
Copy code
class Foo private constructor() {
  companion {
    operator fun invoke(): Foo? = ...
  }
}
c
Considering factory methods only exist to circumvent the arbitrary limitations of constructors that are set by the language, I wonder if it would be better to just relax the restrictions on constructors and make them one and the same like they should be.
If anything, the companion factory idiom adds hidden behavior
y
Hmmm, I can see an argument for adding this to the language:
Copy code
class Foo private constructor() {
  constructor(): Foo? = ...
}
Not sure how it'd work with extension functions though.
c
Semantically (in Kotlin specifically), there's little difference between secondary constructors and factory methods, since everything has to invoke the primary. In that respect, you could see
constructor()
as a shorthand for
static operator fun new(): Foo
, similarly to how
new
invokes an operator function in C++. So if static extensions were ever added, you could probably do
operator fun Foo::new(): Foo
. I wonder how the Java interop would work with that, though. The JVM knows it's not actually a constructor, so it couldn't be invoked like one, which would be weird for Java users reading the source code. It does lend well to Kotlin's primary constructor model, though.
It would also be weird that you could potentially do
val x: Int = String()
, but that's actually already a thing with the Companion.invoke idiom.
I wish there wasn't a distinction, to be honest. It makes me miss C's factory methods. Knowing that that's all it's doing on the backend makes all these restrictions just feel so arbitrary.
Actually, a "factory" keyword would standardize the process, stop the hidden behavior, and ensure there's a distinction to Java and JS users while still allowing the flexibility. (Not that there's even a distinction in JS, since even its constructors are just shorthand for factory methods, but in the mind there's a distinction.)
Copy code
class Foo private constructor() {
  constructor(value: String) : this()

  factory(): Foo? { ... }
  // ^ literally just shorthand for:
  companion { operator fun invoke(): Foo? { ... } }
}
Idk, it's just constant boilerplate in the "Java without boilerplate" language. I've found myself needing to write a nullable
Companion.invoke
method for every single value class I create, since the whole point is to assert they satisfy a condition.
y
I had suggested:
Copy code
companion operator fun invoke(): Foo? { ... }
i.e.
companion
as a modifier. Would that be short enough for you? It's 4 words vs 1 (
companion operator fun invoke
vs
factory
). Kinda reminds me of
public static void main()
from Java lol
c
Interesting idea. That would be nice, having companion as a modifier.
d
Constructors have limitations for a reason, but the JVM may have lost the reasons a while ago. Back in the C++ days, the intent was that when constructors complete, you have a fully usable object. It was an important part of the RAII design pattern. With Java, the main concern was exposing not-yet-initialized fields. This is actually still possible if you're not careful (or malicious) in the constructor itself, passing the "this" reference code outside your class. Including calling methods that may be overridden in sub classes.