9

Kotlin allows to name a function same as an existing class, e.g. HashSet with initializer function could be implemented like this:

fun <T> HashSet(n : Int, fn: (Int) -> T) = HashSet<T>(n).apply {
    repeat(n) {
        add(fn(it))
    }
}

When used, it looks like a normal HashSet constructor:

var real = HashSet<String>()
var fake = HashSet(5) { "Element $it" }

Should this be avoided or encouraged and why?

hotkey
  • 140,743
  • 39
  • 371
  • 326
Vilmantas Baranauskas
  • 6,596
  • 3
  • 38
  • 50

2 Answers2

15

UPD

In the updated coding conventions, there's a section on this topic:

Factory functions

If you declare a factory function for a class, avoid giving it the same name as the class itself. Prefer using a distinct name making it clear why the behavior of the factory function is special. Only if there is really no special semantics, you can use the same name as the class.

Example:

class Point(val x: Double, val y: Double) {
    companion object {
        fun fromPolar(angle: Double, radius: Double) = Point(...)
    }
}

The motivation I described below, though, seems to still hold.


As said in documentation about the naming style:

If in doubt default to the Java Coding Conventions such as:

  • methods and properties start with lower case

One strong reason to avoid naming a function same to a class is that it might confuse a developer who will use it later, because, contrary to their expectations:

  • the function won't be available for super constructor call (if the class is open)
  • it won't be visible as a constructor through reflection
  • it won't be usable as a constructor in Java code (new HashSet(n, it -> "Element " + it) is an error)
  • if you want to change the implementation later and return some subclass instance instead, it will get even more confusing that HashSet(n) { "Element $it" } will construct not a HashSet but, for example, a LinkedHashSet

It's better to show it explicitly that it's a factory function, not a constructor, to avoid this confusion.

Naming a function same to a class is generally avoided in stdlib, too. Given SomeClass, in stdlib a preferred naming style for factory functions is someClassOf, someClassBy or whatever explains the semantics of the function best. The examples:

  • generateSequence { ... } and sequenceOf(...)
  • lazy { ... } and lazyOf(...)
  • compareBy { ... }
  • listOf(...), setOf(...), mapOf(...)

So, one should definitely have strong reason to have a function mimic a constructor.

Instead, a function's name might tell a user more (even everything) about its usage.

hotkey
  • 140,743
  • 39
  • 371
  • 326
  • 1
    really? So now secondary constructors are better than factory functions? I think we need an official opinion (in the docs) – voddan Jan 12 '16 at 23:14
  • 1
    @voddan, the question and the answer are not about secondary constructors, they're about having a function mimic a constructor. What about secondary constructors, they are of no use when a function should return a subclass instance with different implementation. Otherwise, they seem to fit better than factory functions (reflection, super ctor call, Java interop). – hotkey Jan 12 '16 at 23:22
  • well, the sole propose of functions named like classes is factoring those classes without flooding the class code. The alternative is secondary constructors, and it is kind of implied in your answer. – voddan Jan 13 '16 at 07:02
  • @voddan, well, in the answer I just stated that *naming* a factory function same to class may not be a good idea and why it's better to adhere the official naming style, explaining what may follow if one does not. The answer doesn't actually recommend using constructors instead of factory functions. Should I edit the answer to clarify it somehow? – hotkey Jan 13 '16 at 08:33
  • 7
    As the developer responsible for the Kotlin docs, I find this answer to be very sensible guidance. There is no reason to prefer secondary constructors to factory functions, but when you do use factory functions, the code is clearer if you give them distinct names, different from the name of the class. – yole Jan 13 '16 at 08:46
  • @yole, thanks, I updated the answer to emphasize that the points stated are not against factory functions but against the naming style in question. – hotkey Jan 13 '16 at 10:36
  • In context, this originated from this other comment in another question where it was stipulated this was a good idea: http://stackoverflow.com/questions/34738702/is-there-a-way-to-construct-a-hashset-with-initializator-function-in-kotlin#comment57225254_34739302 but I commented that it was a bad idea. You explanation here is nicely done and thorough. – Jayson Minard Jan 13 '16 at 17:09
-2

I agree with +hotkey. It's probably best to avoid confusion in this case.

If it's only used internally and all the other devs (if any) are okay with it, though, I'd say to go for it. Python acknowledges that idea and I love it. Heck, they go both ways, being okay with you naming a class in function case, too, if it feels more like it's acting like a function. But, Python doesn't have to deal with Java interop, so definitely don't do it for public code.

Jacob Zimmerman
  • 1,521
  • 11
  • 20
  • 3
    I don't think we should pull in ideals of other languages as answers for "what is best in Kotlin". And internal code doesn't change that fact that it should follow best practices across the industry unless there is a good enough reason to differ. – Jayson Minard Jan 13 '16 at 17:11
  • +Jayson Minard First, I want to point out that I mostly agree. Secondly, I think that naming conventions, pertaining to capitalisation, doesn't really count as a "best practice". For example, C# and Java are almost the same language yet have different capitalization conventions. The reason for these conventions is purely about consistency for the sake of readability within a certain language community. So, I stick with what I said. If everyone who reads that code is okay with it, then there's nothing wrong with it. This is the same as any other convention mandated by a company style sheet. – Jacob Zimmerman Jan 16 '16 at 04:38
  • This is a bit more than about capitalization. – Jayson Minard Jan 17 '16 at 18:09
  • How so? That's all I see here. – Jacob Zimmerman Jan 18 '16 at 12:05
  • 1
    The question is whether it is ok to make an extension function appear to be a constructor. Other answers talk about details of naming conventions including capitalization, but that isn't the question. – Jayson Minard Jan 18 '16 at 12:09
  • Okay, even on that note, I still stick with what I said, because it's implicitly tied in with the answer. Also, it's explicitly tied in with what I said about Python. You claim that one shouldn't bring other languages' conventions over, and I maintain that, as long as all users of the code agree, there's no reason not to. The only potential problem I see is the *compiler* ambiguity of it not knowing which HashSet to call. – Jacob Zimmerman Jan 18 '16 at 12:14