- Nomenclature
- Declarations
- Spacing
- Semicolons
- Getters & Setters
- Brace Style
- When Statements
- Types
- Language
On the whole, naming should follow Java standards, as Kotlin is a JVM-compatible language.
Package names are similar to Java: all lower-case, multiple words concatenated together, without hypens or underscores:
BAD:
com.RayWenderlich.funky_widgetGOOD:
com.raywenderlich.funkywidgetWritten in UpperCamelCase. For example RadialSlider.
Written in lowerCamelCase. For example setValue.
Generally, written in lowerCamelCase. Fields should not be named with Hungarian notation, as Hungarian notation is erroneously thought to be recommended by Google.
Example field names:
class MyClass {
var publicField: Int = 0
val person = Person()
private var privateField: Int?
}Constant values in the companion object should be written in uppercase, with an underscore separating words:
companion object {
const val THE_ANSWER = 42
}Written in lowerCamelCase.
Single character values must be avoided, except for temporary looping variables.
In code, acronyms should be treated as words. For example:
BAD:
XMLHTTPRequest
URL: String?
findPostByIDGOOD:
XmlHttpRequest
url: String
findPostByIdOnly include visibility modifiers if you need something other than the default of public.
BAD:
public val wideOpenProperty = 1
private val myOwnPrivateProperty = "private"GOOD:
val wideOpenProperty = 1
private val myOwnPrivateProperty = "private"Access level modifiers should be explicitly defined for classes, methods and member variables.
Prefer single declaration per line.
GOOD:
username: String
twitterHandle: StringExactly one class per source file, although inner classes are encouraged where scoping appropriate.
If we want to use the arguments passed through the constructor, we can do it by defining the properties, these properties can be mutable or immutable and they help us to define in a simple way the famous Java getters and setters fields, but with a big difference, the simplicity.
- Properties without modifying its get and set method
BAD:
class Person(name: String, age: Int) {
val name: String
val age: Int
init {
this.name = name
this.age = age
}
}BAD:
class Person(name: String, age: Int) {
val name: String = name
val age: Int = age
}GOOD:
class Person(val name: String, val age: Int)
- Properties modifying its get and set method.
You don't need to move all the arguments to the body of the class, only the one that will be modified.
BAD:
class Person(name: String, age: Int) {
val age = age
var name = name
get() = "Su nombre es: $field"
set(value) { field = "Nuevo $value" }
}GOOD:
class Person(name: String, val age: Int) {
var name = name
get() = "Su nombre es: $field"
set(value) { field = "Nuevo $value" }
}
Prefer data classes for simple data holding objects.
BAD:
class Person(val name: String) {
override fun toString() : String {
return "Person(name=$name)"
}
}GOOD:
data class Person(val name: String)Enum classes without methods may be formatted without line-breaks, as follows:
private enum class CompassDirection { EAST, NORTH, WEST, SOUTH }Declare a LiveData as public and associate it with a private MutableLiveData, it is true that you can work it defining and observing only a MutableLiveData, but this is not a good practice, since the logic and values that are bound to that MutableLiveData could be modified by other classes from outside. Remember that LiveData are read only and are perfect to be observed by other classes.
BAD:
val progressVisible: MutableLiveData<Boolean> by lazy {
MutableLiveData<Boolean>()
}GOOD:
private val _progressVisible = MutableLiveData<Boolean>()
val progressVisible: LiveData<Boolean> get() = _progressVisibleSpacing is especially important in raywenderlich.com code, as code needs to be easily readable as part of the tutorial.
Indentation is using spaces - never tabs.
Indentation for blocks uses 2 spaces (not the default 4):
BAD:
for (i in 0..9) {
Log.i(TAG, "index=" + i)
}GOOD:
for (i in 0..9) {
Log.i(TAG, "index=" + i)
}Indentation for line wraps should use 4 spaces (not the default 8):
BAD:
val widget: CoolUiWidget =
someIncrediblyLongExpression(that, reallyWouldNotFit, on, aSingle, line)GOOD:
val widget: CoolUiWidget =
someIncrediblyLongExpression(that, reallyWouldNotFit, on, aSingle, line)Lines should be no longer than 100 characters long.
There should be exactly one blank line between methods to aid in visual clarity and organization. Whitespace within methods should separate functionality, but having too many sections in a method often means you should refactor into several methods.
When they are needed, use comments to explain why a particular piece of code does something. Comments must be kept up-to-date or deleted.
Avoid block comments inline with code, as the code should be as self-documenting as possible. Exception: This does not apply to those comments used to generate documentation.
Semicolons are dead to us should be avoided wherever possible in Kotlin.
BAD:
val horseGiftedByTrojans = true;
if (horseGiftedByTrojans) {
bringHorseIntoWalledCity();
}GOOD:
val horseGiftedByTrojans = true
if (horseGiftedByTrojans) {
bringHorseIntoWalledCity()
}Unlike Java, direct access to fields in Kotlin is preferred.
If custom getters and setters are required, they should be declared following Kotlin conventions rather than as separate methods.
Only trailing closing-braces are awarded their own line. All others appear the same line as preceding code:
BAD:
class MyClass
{
fun doSomething()
{
if (someTest)
{
// ...
}
else
{
// ...
}
}
}GOOD:
class MyClass {
fun doSomething() {
if (someTest) {
// ...
} else {
// ...
}
}
}Conditional statements are always required to be enclosed with braces, irrespective of the number of lines required.
BAD:
if (someTest)
doSomething()
if (someTest) doSomethingElse()GOOD:
if (someTest) {
doSomething()
}
if (someTest) { doSomethingElse() }Unlike switch statements in Java, when statements do not fall through. Separate cases using commas if they should be handled the same way. Always include the else case.
BAD:
when (anInput) {
1 -> doSomethingForCaseOneOrTwo()
2 -> doSomethingForCaseOneOrTwo()
3 -> doSomethingForCaseThree()
}GOOD:
when (anInput) {
1, 2 -> doSomethingForCaseOneOrTwo()
3 -> doSomethingForCaseThree()
else -> println("No case satisfied")
}Always use Kotlin's native types when available. Kotlin is JVM-compatible so [TODO: more info]
Type inference should be preferred where possible to explicitly declared types.
BAD:
val something: MyType = MyType()
val meaningOfLife: Int = 42GOOD:
val something = MyType()
val meaningOfLife = 42Constants are defined using the val keyword, and variables with the var keyword. Always use val instead of var if the value of the variable will not change.
Tip: A good technique is to define everything using val and only change it to var if the compiler complains!
** TODO: A bunch of stuff about companion objects **
Declare variables and function return types as nullable with ? where a null value is acceptable.
Use implicitly unwrapped types declared with !! only for instance variables that you know will be initialized before use, such as subviews that will be set up in onCreate for an Activity or onCreateView for a Fragment.
When naming nullable variables and parameters, avoid naming them like nullableString or maybeView since their nullability is already in the type declaration.
When accessing a nullable value, use the safe call operator if the value is only accessed once or if there are many nullables in the chain:
editText?.setText("foo")Use en-US English spelling. 🇺🇸
BAD:
val colourName = "red"GOOD:
val colorName = "red"