Skip to content

Kotlin Conventions for Android

Quoc DEV edited this page Sep 4, 2017 · 16 revisions

Kotlin Conventions for Android

Table of Contents


The rules

  • BAD: ❌ not recommend
  • GOOD: ✅ recommend

Naming Style

On the whole, naming should follow Java standards, as Kotlin is a JVM-compatible language.

Packages

Package names are similar to Java: all lower-case, multiple words concatenated together, without hypens or underscores:

BAD: ❌

package Vn.Asiantech.Android

GOOD: ✅

package vn.asiantech.android

Classes & Interfaces

Written in UpperCamelCase. ✅

For example HomeActivity, MainFragment...

Methods

Written in lowerCamelCase. ✅

For example setData, getApiNews...

Fields

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
}

Variables & Parameters

Written in lowerCamelCase. ✅

Single character values must be avoided, except for temporary looping variables.

Misc

In code, acronyms should be treated as words. For example:

BAD: ❌

XMLHTTPRequest
URL: String? 
findPostByID

GOOD: ✅

XmlHttpRequest
url: String
findPostById

Spacing

Indentation

Indentation is using spaces - never tabs.

Blocks

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)
}

Line Wraps

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)

Line Break

  • Lines should be no longer than 130 characters long.(for Android studio)

  • Start a new line by a symbol which are ,, :, {, = in case characters over vertical line.

For example: ✅

fun setData(name: String, age: Int, class: String, date: String, message: String,
     time: String) {
}

fun getInfo(name: String, birth: String, age: Int, weight: Float, hight: Float) =
   getInfo().apply {
   }

data class Animal(private val weight: Float, private val hight: Float) :
         AbstractAnimal() {
}

Vertical Spacing

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.

Colon

  • There is a space before colon(:) where colon separates type and supertype and there's no space where colon separates instance and type.

BAD:

interface Foo<out T:Any>: Bar {
    fun foo(a : Int):T
}

// or declaring a variable's type

val firstName:String

GOOD:

 interface Foo<out T : Any> : Bar {
    fun foo(a: Int): T
}

// or declaring a variable's type

val firstName: String
  • When defining a class always put a space between the closing parenthesis of the primary constructor and the opening bracket of the class body. Parameters in the default constructor should each appear on their own line:

BAD:

class User(public open var firstName: String, public open var lastName: String){}

GOOD:

class User(
    public open var firstName: String,
    public open var lastName: String
) {}
  • If your class is either a subclass or implements an interface the standard formatting rules for type declarations still apply. There should be a space between the : and name of the parent class, but not between the primary constructor and the ::

GOOD:

class User(
    public open var firstName: String,
    public open var lastName: String
): RealmObject() {}

Semicolons

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()
}

Brace Style

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() }

Types

Always use Kotlin's native types when available. Kotlin is JVM-compatible so:

Unit

If at all possible, ignore explicit usage of the Unit type. Most commonly this would be in the case of a function that returns Unit.

BAD:

fun doSomething(): Unit

GOOD:

fun doSomething()

Type Inference

Type inference should be preferred where possible to explicitly declared types.

BAD:

val something: MyType = MyType()
val meaningOfLife: Int = 42

GOOD:

val something = MyType()
val meaningOfLife = 42

Constants vs. Variables

Constants 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!

Companion Objects

Companion objects should always be defined immediately at the top of the class. Naming conventions for val within a companion object should follow our Java coding standards.

BAD:

class MainFragment: Fragment() {
    companion object {
        val TypeViewHeader = 0
        val TypeViewFooter = 1
    }
}

GOOD:

class MainFragment: Fragment() {
    companion object {
        const val TYPE_VIEW_HEADER = 0
        const val TYPE_VIEW_FOOTER = 1
    }
}

Strings

Always prefer string interpolation if possible.

BAD:

val fullName = user.firstName + " " + user.lastName

GOOD:

val fullName = "${user.firstName} ${user.lastName}"

Optionals

Declare variables and function return types as optional 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 optional variables and parameters, avoid naming them like optionalString or maybeView since their optional-ness is already in the type declaration.

When accessing an optional value, use optional chaining if the value is only accessed once or if there are many optionals in the chain:

editText?.setText("foo")

Idioms

Use an IDE suggestion

BAD:

val array = ArrayList<Int>()
array.get(0)

// or

getActivity().finish()

GOOD:

val array = ArrayList<Int>()
array[0]

// or

activity().finish()

it in Lambda expression applies to wide scope

BAD:

{ animal -> 
  animal?.let { it.foo() }
}

GOOD:

{ 
  it?.let { animal -> animal.foo() }
}

Should use a type interface

You can write a type if it is difficult to understand.

  • Property
val age = 0       // Int
val foo = 10L     // Long
val height = 100f // Float
  • Function
// return Boolean
fun Context.isConnectToWifi() =
      (getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager)
         .activeNetworkInfo?.type == ConnectivityManager.TYPE_WIFI

// return Point
fun Display.getSize(): Point = Point().apply { getSize(this) }

Don't write for loop

You don't have to write for loop because there is forEach in collections package of Kotlin.

BAD:

for (i in 0..9) {
  // todo somethings
}

GOOD:

(0..9).forEach {
// todo somethings
}

// or if you want to know index

(0..9).forEachIndexed { index, value ->
// todo somethings
}

Use to expression creating Pair class

BAD:

val pair = Pair(foo, bar) 

GOOD:

val pair = foo to bar

Use a smart cast

BAD:

if (dog !is Animal) {
    throw IllegalArgumentException("not Animal!")
}
dog.foo()

GOOD:

dog as? Animal ?: throw IllegalArgumentException("not Animal!")
dog.foo()

Getters & Setters

Unlike Java, direct access to fields in Kotlin is preferred.

For example:

GOOD:

// syntax
var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

// a custom setter looks like this
var stringRepresentation: String
    get() = this.toString()
    set(value) {
        setDataFromString(value) // parses the string and assigns values to other properties
    }

If custom getters and setters are required, they should be declared following Kotlin conventions rather than as separate methods.

When Statements

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 -> doSomethingForCaseOne()
  2 -> doSomethingForCaseOneOrTwo()
  3 -> doSomethingForCaseThree()
}

GOOD:

when (anInput) {
  1, 2 -> doSomethingForCaseOneOrTwo()
  3 -> doSomethingForCaseThree()
  else -> println("No case satisfied")
}

Visibility Modifiers

Only 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

Access level modifiers should be explicitly defined for classes, methods and member variables.

Fields & Variables

Prefer single declaration per line.

GOOD: ✅

username: String
twitterHandle: String

Classes

Exactly one class per source file, although inner classes are encouraged where scoping appropriate.

Data Type Objects

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

TODO: UPDATE FOR KOTLIN (This is java-only and may not be true with Kotlin)

Enum classes should be avoided where possible, due to a large memory overhead. Static constants are preferred. See http://developer.android.com/training/articles/memory.html#Overhead for further details.

Enum classes without methods may be formatted without line-breaks, as follows:

private enum CompassDirection { EAST, NORTH, WEST, SOUTH }

Annotations & Lambdas

Annotations

Standard annotations should be used - in particular override. This should appear on the same line as the function declaration.

BAD:

fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState);
}

GOOD:

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState);
}

Annotations should appear above a class definition, and inline if it is annotating a field in a class:

BAD:

@Root(strict=false) public open class User (
    @field:Element 
    public open var firstName: String? = "",
    @field:Element 
    public open var lastName: String? = "",
) {...}

GOOD:

@Root(strict=false) 
public open class User (
    @field:Element public open var firstName: String? = "",
    @field:Element public open var lastName: String? = "",
) {...}

Lambdas

Coming soon

XML Guidance

Since Android uses XML extensively in addition to Java, we have some rules specific to XML.

XML File Names

View-based XML files should be prefixed with the type of view that they represent.

BAD:

  • login.xml
  • main_screen.xml
  • rounded_edges_button.xml

GOOD:

  • activity_login.xml
  • fragment_main_screen.xml
  • button_rounded_edges.xml

Indentation

Similarly to Java, indentation should be two characters.

Use Context-Specific XML Files

Wherever possible XML resource files should be used:

  • Strings => res/values/strings.xml
  • Styles => res/values/styles.xml
  • Colors => res/color/colors.xml
  • Animations => res/anim/
  • Drawable => res/drawable

XML Attribute Ordering

Where appropriate, XML attributes should appear in the following order:

  • id attribute
  • layout_* attributes
  • style attributes such as gravity or textColor
  • value attributes such as text or src

Within each of these groups, the attributes should be ordered alphabetically.

Others

Language

Use en-US English spelling. 🇺🇸

BAD:

val colourName = "red"

GOOD:

colorName = "red"

Copyright Statement

The following copyright statement should be included at the top of every source file:

/*
 * Copyright (c) 2017 `Your company`
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * Notwithstanding the foregoing, you may not use, copy, modify, merge, publish, 
 * distribute, sublicense, create a derivative work, and/or sell copies of the 
 * Software in any work that is designed, intended, or marketed for pedagogical or 
 * instructional purposes related to programming, coding, application development, 
 * or information technology.  Permission for such use, copying, modification,
 * merger, publication, distribution, sublicensing, creation of derivative works, 
 * or sale is expressly withheld.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

RxJava & RxAndroid

BAD:

Observable.just(10)
       .map { 
          it * 3 
       } 
       .filter {
          val rand = Random()
          it % rand.nextInt(10) == 0
       }        
       .map { it.toFloat() }
       .withLatestFrom(Observable.just("foo"), BiFunction { num: Float, foo: String -> num to foo })
       .subscribe ({ print(it) }, { print(it) })

// or you must start a new line as operators

Observable.just(10).map { it * 3 } 
       .filter {
          val rand = Random()
          it % rand.nextInt(10) == 0
       }.map { it.toFloat() }
       .withLatestFrom(Observable.just("hoge"),
          BiFunction { num: Float, hoge: String -> num to hoge })
       .subscribe ({
          print(it) 
       }, {
          print(it)
       })

GOOD:

Observable.just(10)
        .map { it * 3 } // one line
        .filter { // line break in case of multiple lines
           val rand = Random()
           it % rand.nextInt(10) == 0
        }        
        .map(Int::toFloat) // method reference
        .withLatestFrom(Observable.just("foo")) { num, hoge -> num to foo } // Use rx-kotlin
        .subscribe ({ // start a new line in subscribe block if it implements `onError`
           print(it) 
        }, {
           print(it)
        })

The latest release (31/8/2017)

Foreign Language

Kotlin Programming Language

Clone this wiki locally