- IDE's Code Style Settings
- Классы
- Условия
- Лямбда функции
Tabs and Indents
- Tab size =
4
- Indent =
4
- Continuation indent =
4
(default8
)
Wrapping and Braces
Wrap always
- Extends/implements list align when multiline =
true
(defaultfalse
)
Шапка класса форматируется следующим образом:
class SimpleClass {
var someProperty: String = "I'm property!"
fun someMethod() {
println(someProperty)
}
}
Если в конструкторе класса присутствует 1 значение или свойство, то она располагается на одной строке с кодовым словом class.
class SimpleClass(var someProperty: String) {
fun someMethod() {
println(someProperty)
}
}
В случаях, когда строка получается слишком длинная следует переносить.
class VeryVeryLongNamedClass(
var someLongNamedProperty: String
) {
fun someMethod() {
println(someProperty)
}
}
Если в конструкторе класса присутствует более 1 значения или свойства, то они располагается друг под другом, начиная со следующей строки после строки с ключевым словом class. Закрывающая скобка переносится на следующую строку.
class SimpleClass(
var someProperty: String,
val anotherProperty: Int
) {
fun someMethod() {
println("$someProperty($anotherProperty)")
}
}
Если в конструкторе класса потомка нет значений и свойств, то родительский класс указывается на одной строке с ключевым словом class.
class SimpleClass : SuperSimpleClass() {
fun someMethod() {
// do something
}
}
Интерфейсы, которые реализует класс-наследник, должны быть расположены друг под другом:
class SimpleClass : SuperSimpleClass(),
IFirst,
ISecond {
fun someMethod() {
// do something
}
}
// Форматирование здорового человека
class MyClass(
private var myProperty: Boolean,
someProperty: String,
anotherProperty: Int
) : SimpleClass(someProperty, anotherProperty),
IFirst,
ISecond {
override fun someMethod() {
super.someMethod()
println(myProperty)
}
}
// Не делайте так, форматирование курильщика :(
class MyClass(
private var myProperty: Boolean,
someProperty: String,
anotherProperty: Int) : SimpleClass(someProperty, anotherProperty),
IFirst,
ISecond{
override fun someMethod() {
super.someMethod()
println(myProperty)
}
}
// Не делайте так, форматирование курильщика :(
class MyClass(
private var myProperty: Boolean,
someProperty: String,
anotherProperty: Int
) : SimpleClass(someProperty, anotherProperty), IFirst, ISecond {
override fun someMethod() {
super.someMethod()
println(myProperty)
}
}
https://kotlinlang.org/docs/reference/coding-conventions.html#class-layout
Примерный перевод:
Содержимое класса сортируется в следующем порядке:
- Описание свойств и блоки инициализации
- Вторичные конструкторы
- Описание методов*
- Объект компаньон
- Не сортируйте описания методов в алфавитном или модификаторно видимом порядке и не отделяйте обычные методы от методов расширения. Вместо этого, размещайте связанные блоки вместе, так чтобы при чтении класса сверху вниз было возможно отслеживать логику того, что происходит. Выбирайте порядок (Либо сперва вещи более высокого уровня, либо наоборот) и придерживайтесь его.
- Размещайте внутренние классы в том порядке, в котором они используются в коде. Если эти классы предназначены для их использования снаружи и не имеют связи внутри класса, то размещайте такие классы после объекта компаньона.
- При реализации интерфейса следует реализовывать их методы в том же порядке, что и они сами (по необходимости, среди них размещаются приватные методы, которые необходимы для реализации интеферфейсов).
class ExampleActivity : AppCompatActivity() {
private var somePropertie: String = ""
private val anotherPropertie: Int = 20
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
initViews()
}
private fun initViews() {
// Doing some magic
}
fun someFun() {
// Doing some Fun
}
companion object {
const val EXAMPLE_KEY = "extra"
const val MAX_COUNT_KEY = "extra"
const val EXAMPLE_REQUEST_CODE = 100
fun createIntent(): Intent {
return Intent()
}
}
}
В ходе безжалостной схватки было решено, что единственно правильно всегда возвращать значение фукнции через кодовое слово return, игнорируя все прелести Котлина.
// Так должны выглядеть функции в нашей команде
fun providesCargoRepository(cargoApi: ICargoApi): ICargoRepository {
return CargoRepository(cargoApi)
}
// Так Котлин развращает писать невинных девелоперов
fun providesCargoRepository(cargoApi: ICargoApi): ICargoRepository = CargoRepository(cargoApi)
fun providesCargoRepository(cargoApi: ICargoApi) = CargoRepository(cargoApi)
Так как фигурные скобки бесплатные, то нам не жалко всегда заключать тело условий в красивые фигурные скобки. А бонусом они повышают читабильность кода :3
// Делай так ^_^
if (someTest) {
doSomething()
}
// И так делай тоже :)
if (someTest) {
doSomething()
} else {
doSomethingElse()
}
// Но вот так лучше не делать :<
if (someTest) doSomething()
if (someTest) doSomething() else doSomethingElse()
if (someTest)
doSomething()
else
doSomethingElse()
Guard Statement помогает нам быстро убежать из функции, если что-то пошло не так. Поэтому мы хотим видеть его на одной строчке. Если быстро убежать не получается, то не стесняемся применять фигурные скобочки :)
// Быстро проверили на null и убежали - красота
fun example(nullableArg: String?) {
if (nullableArg == null) return
doSomething()
}
// Пример с правильным разNULLением проперти
var someProperty: String? = null
fun example() {
val someProperty = someProperty ?: return
doSomething(someProperty)
}
// А вот тут уже неправильно!
fun example(nullableArg: String?) {
if (nullableArg == null)
return
doSomething()
}
// И так тоже неправильно :(
fun example(nullableArg: String?) {
if (nullableArg == null) {
return
}
doSomething()
}
В языке Kotlin нет тернарного опратора, но он позволяет получать результат из операторов if
и when
. Поэтому используем одностроную конструкцию if-else вместо него.
// Фигачим все в одну строку как-будто это тернарный оператор ^_^
val colorRes = if(someTest) R.color.abc_color_highlight_material else R.color.cardview_shadow_end_color
// Если добавить переносы, то выглядит уже не так правильно
val colorRes = if(someTest)
R.color.abc_color_highlight_material
else
R.color.cardview_shadow_end_color
// И даже фигурные скобочки не могут вернуть элегантность
val colorRes = if(someTest) {
R.color.abc_color_highlight_material
} else {
R.color.cardview_shadow_end_color
}
В Котлине, если последний параметр функции это функция и когда в качестве этого параметра передается лямбда выражение, есть возможность вынесения лямбда выражения за круглые скобки функции. Выглядит очень классно, поэтому мы активно этим пользуемся.
Для примера, у нас есть класс SimpleClass
, у которого проперти это функция и у нас есть глобальная функция doIfSdk
, у которого последний параметр это функция.
class SimpleClass(val block: () -> Unit) {
fun callBlock() {
block.invoke()
}
}
fun doIfSdk(version: Int, block: () -> Unit) {
if (version >= BuildConfig.VERSION_CODE) {
block.invoke()
}
}
// Форматирование у нас должно быть как в следующей фукнции:
fun outside() {
val simpleObject = SimpleClass {
/*do something*/
}
val simpleBigObject = SimpleClass {
/* do
something
big */
}
doIfSdk(21) { /*do something*/ }
doIfSdk(21) {
/* do
something
big */
}
}
// И ни в коем случае не такое как в этой
fun inside() {
val simpleObject = SimpleClass({ /*do something*/ })
val simpleBigObject = SimpleClass({
/* do
something
big */
})
doIfSdk(21, { /*do something*/ })
doIfSdk(21, {
/* do
something
big */
})
}
В нашей команде решили не заморачиваться с правилами насчет максимальной длины тела лямбда функции. Решили только руководствоваться здравым смыслом и выносить куски по мере необходимости.
Лямбда функции без параметров ((() -> Unit)
) решили по возможности писать в одну строку.
// Вот так вот:
button.onClick { oneAction() }
button.onClick {
firstAction()
secondAction()
}
// А не вот так:
button.onClick {
oneAction()
}
В лямбда функциях с одним параметром решили не злоупотреблять it
и всегда явно указывать параметр. Для повышения читабильности мы так же переносим тело функции на следующую от параметра строку.
// Вот так короч надо
function { result ->
showResult(result)
}
// А вот так лучше не надо
function {
result ->
showResult(result)
}
// Вот так точно не стоит
function { result -> showResult(result) }
В лямбда функциях с несколькими параметрами решили для повышения читабильности мы так же переносим тело функции на следующую от параметров строку.
// Т.е. продолжаем писать так
fetchData { count, title ->
setCountAndTitle(title, count)
}
// И не пытаемся делать так
fetchData { count,
title ->
setCountAndTitle(title, count)
}