Должны ли мы избегать называть функцию так же, как существующий класс в Kotlin? Почему?

Kotlin позволяет назвать функцию так же, как существующий класс, например. HashSet с функцией инициализации можно реализовать так:

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

При использовании он выглядит как обычный конструктор HashSet:

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

Следует ли этого избегать или поощрять и почему?


person Vilmantas Baranauskas    schedule 12.01.2016    source источник


Ответы (2)


UPD

В обновленных соглашениях о кодировании есть раздел по этой теме. :

Заводские функции

Если вы объявляете фабричную функцию для класса, не давайте ей то же имя, что и сам класс. Лучше использовать отдельное имя, чтобы было понятно, почему поведение фабричной функции особенное. Только если особой семантики действительно нет, можно использовать то же имя, что и у класса.

Пример:

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

Тем не менее, мотивация, которую я описал ниже, кажется, по-прежнему актуальна.


Как сказано в документации по стилю именования:

Если вы сомневаетесь, по умолчанию используйте соглашения о кодировании Java, такие как:

  • методы и свойства начинаются с нижнего регистра

Одной из веских причин избегать имен функций, идентичных классу, является то, что это может запутать разработчика, который будет использовать его позже, потому что, вопреки их ожиданиям:

  • функция не будет доступна для вызова суперконструктора (если класс open)
  • он не будет виден как конструктор через отражение
  • его нельзя использовать в качестве конструктора в коде Java (new HashSet(n, it -> "Element " + it) — ошибка)
  • если вы захотите позже изменить реализацию и вместо этого вернуть некоторый экземпляр подкласса, будет еще более запутанно, что HashSet(n) { "Element $it" } создаст не HashSet, а, например, LinkedHashSet

Лучше явно показать, что это фабричная функция, а не конструктор, чтобы избежать путаницы.

В stdlib также обычно избегают именования функции, аналогичной классу. Учитывая SomeClass, в stdlib предпочтительным стилем именования для фабричных функций является someClassOf, someClassBy или что-то еще, что лучше всего объясняет семантику функции. Примеры:

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

Таким образом, у человека определенно должны быть веские причины для того, чтобы функция имитировала конструктор.

Вместо этого имя функции может рассказать пользователю больше (даже обо всем) о ее использовании.

person hotkey    schedule 12.01.2016
comment
В самом деле? Итак, теперь вторичные конструкторы лучше, чем фабричные функции? Я думаю, нам нужно официальное мнение (в документах) - person voddan; 13.01.2016
comment
@voddan, вопрос и ответ не о вторичных конструкторах, а о том, чтобы функция имитировала конструктор. Что касается вторичных конструкторов, они бесполезны, когда функция должна возвращать экземпляр подкласса с другой реализацией. В противном случае они, кажется, подходят лучше, чем фабричные функции (отражение, вызов super ctor, взаимодействие Java). - person hotkey; 13.01.2016
comment
ну, единственное предложение функций, названных как классы, - это факторизация этих классов без переполнения кода класса. Альтернативой являются вторичные конструкторы, и это как бы подразумевается в вашем ответе. - person voddan; 13.01.2016
comment
@voddan, ну, в ответе я только что заявил, что именование фабричной функции, аналогичной классу, может быть плохой идеей, и почему лучше придерживаться официального стиля именования, объяснив, что может последовать, если кто-то это сделает. нет. Ответ на самом деле не рекомендует использовать конструкторы вместо фабричных функций. Должен ли я отредактировать ответ, чтобы как-то уточнить его? - person hotkey; 13.01.2016
comment
Как разработчик, ответственный за документы Kotlin, я считаю этот ответ очень разумным руководством. Нет причин предпочитать вторичные конструкторы фабричным функциям, но когда вы используете фабричные функции, код становится более понятным, если вы даете им разные имена, отличные от имени класса. - person yole; 13.01.2016
comment
@yole, спасибо, я обновил ответ, чтобы подчеркнуть, что указанные пункты не против заводских функций, а против рассматриваемого стиля именования. - person hotkey; 13.01.2016
comment
В контексте это произошло из этого другого комментария в другом вопросе, где было указано, что это хорошая идея: stackoverflow.com/questions/34738702/, но Я прокомментировал, что это была плохая идея. Ваше объяснение здесь хорошо сделано и тщательно. - person Jayson Minard; 13.01.2016

Я согласен с горячей клавишей +. Вероятно, в этом случае лучше избегать путаницы.

Если он используется только внутри и все остальные разработчики (если таковые имеются) согласны с этим, я бы сказал, что нужно пойти на это. Python признает эту идею, и мне она нравится. Черт возьми, они идут в обоих направлениях, и вы тоже можете называть класс в функциональном регистре, если это больше похоже на то, что он действует как функция. Но Python не должен иметь дело с взаимодействием Java, поэтому определенно не делайте этого для общедоступного кода.

person Jacob Zimmerman    schedule 13.01.2016
comment
Я не думаю, что мы должны использовать идеалы других языков в качестве ответов на то, что лучше всего в Котлине. И внутренний код не меняет того факта, что он должен следовать лучшим практикам в отрасли, если нет достаточно веских причин для отклонения. - person Jayson Minard; 13.01.2016
comment
+ Джейсон Минард Во-первых, я хочу отметить, что в основном согласен. Во-вторых, я думаю, что соглашения об именах, касающиеся использования заглавных букв, на самом деле не считаются лучшей практикой. Например, C# и Java — это почти один и тот же язык, но у них разные правила использования заглавных букв. Причина этих соглашений заключается исключительно в согласованности ради удобочитаемости в определенном языковом сообществе. Итак, я придерживаюсь того, что сказал. Если все, кто читает этот код, согласны с ним, то в этом нет ничего плохого. Это то же самое, что и любое другое соглашение, предусмотренное таблицей стилей компании. - person Jacob Zimmerman; 16.01.2016
comment
Это немного больше, чем о капитализации. - person Jayson Minard; 17.01.2016
comment
Как так? Это все, что я вижу здесь. - person Jacob Zimmerman; 18.01.2016
comment
Вопрос в том, можно ли сделать так, чтобы функция расширения выглядела как конструктор. В других ответах говорится о деталях соглашений об именах, включая использование заглавных букв, но вопрос не в этом. - person Jayson Minard; 18.01.2016
comment
Хорошо, даже на этой ноте я все еще придерживаюсь того, что сказал, потому что это неявно связано с ответом. Кроме того, это явно связано с тем, что я сказал о Python. Вы утверждаете, что не следует привносить соглашения других языков, и я утверждаю, что, пока все пользователи кода согласны, нет причин не делать этого. Единственная потенциальная проблема, которую я вижу, это двусмысленность компилятора, когда он не знает, какой HashSet вызывать. - person Jacob Zimmerman; 18.01.2016