Kotlin is NOT Like Java
BASIC SYNTAX
Properties
Kotlin
public class MyKotlinClass {
   val a: Int = 1
   var b: Int = 1
   val c = 1
   var d = 1
       private set
}

fun example() {
   val myClass = MyKotlinClass()
   myClass.b = 2
}
Java
public class MyJavaClass {
   private final int a = 1;
   private int b = 1;
   private final int c = 1;
   private int d = 1;

   public int getA() { return a; }
   public int getB() { return b; }
   public void setB(int b) { this.b = b; }
   public int getC() { return c; }
   public int getD() { return d; }
   private void setD(int d) { this.d = d; }
}

class ExampleClass {
   public static void example() {
      MyJavaClass myClass = new MyJavaClass()
      myClass.setB(2)
   }
}
String Templates
Kotlin
class MyKotlinClass {
   val name = "Omar"
   val surname = "Miatello"
   val example = "My name is $name $surname"
}
Java
class MyJavaClass {
   final String getName() {
       return "Omar";
   }

   final String getSurname() {
       return "Miatello";
   }

   final String getExample() {
       return String.format("My name is %s %s",
           getName(), getSurname()); }
}
Get and Set
Kotlin
fun renderItem(item: SomeItem) {
    visitCount.text = "${item.visitCount}次拜访"

    phone1.visibility = 
            if(item.cellPhone.isNullOrEmpty()) 
               View.GONE else View.VISIBLE

    shopName.text = item.name ?: "暂无商户名称"
}
Java
void renderItem() {
    visitCount.setText(String.format(...), ...)
         
    if(item.cellPhone == null ||
       item.cellPhone.isEmpty()) {
        phone1.setVisibility(View.GONE)
    } else {
	  phone1.setVisibility(View.VISIBLE)
    }

    shopName.text = 
        item.name != null ? item.name : "..."
}
Data Class
Kotlin
data class User(val name: String, val email: String)
Java
public class User {
   private String name;
   private String email;

   User(String name, String email) {
       this.name = name;
       this.email = email;
   }

   public String getName() {
       return name;
   }
   public void setName(String name) {
       this.name = name;
   }
   public String getEmail() {
       return email;
   }
   public void setEmail(String email) {
       this.email = email;
   }
}
NULL SAFETY
Null Safety
Kotlin
class MyKotlinClass {
   var a: String = "ciao"
   var b: String = null  // Error at compile time
   var c: String? = null
   var d: String? = "ok"

   fun example(e: String, f: String?) {
       e.length()
       f.length()        // Error at compile time
       f?.length()

       if (f != null) {
           f.length()
       }
   }
}
Java
static class MyUtils {
   void example(String e, String f) {
       e.length(); // throw NullPointerException?
       f.length(); // throw NullPointerException?

       if (e != null) {
           e.length();
       }

       if (f != null) {
           e.length();
       }
   }
}
FUNCTION AND LAMBDA
Functions
Kotlin
val a = 1
val b = 2

val f1 = { a: Int, b: Int -> a + b }

fun f2(a: Int, b: Int): Int {
   return a + b
}

fun f2_(a: Int, b: Int): Int = a + b

fun f3(a: Int, b: Int, fn: (Int, Int) -> Int): Int =
       if (a > b) fn(a, b) else a - b

class MyKotlinClass {
   fun test() {
       print(f1(a, b))         // => 3
       print(f2(a, b))         // => 3
       print(f3(a, b, f1))     // => -1
       print(f3(2, 1, ::f2))   // => 3
   }
}
Java
public class SomeJavaClass {

   static int add(int a, int b) {
       return a + b;
   }

   interface Addable {
       int add(int a, int b);
   }

   static int add2(int a, int b, Addable addable) {
       if (a > b) {
           return addable.add(a, b);
       } else {
           return a - b;
       }
   }

   void test() {
       System.out.println(add2(2, 1, new Addable() {
           @Override
           public int add(int a, int b) {
               return a + b;
           }
       }));
   }
}
Lambda
Kotlin
fun f3(a: Int, b: Int, fn: (Int, Int) -> Int): Int =
       if (a > b) fn(a, b) else a - b

fun test() {
   f3(2, 1) { a1, a2 ->
       a1 + a2
   }                               // => 3       
   f3(2, 1, { a1, a2 -> a1 + a2})  // => 3
}
Java
public class SomeJavaClass {

   interface Addable {
       int add(int a, int b);
   }

   static int add2(int a, int b, Addable addable) {
       if (a > b) {
           return addable.add(a, b);
       } else {
           return a - b;
       }
   }

   void test() {
       System.out.println(add2(2, 1, new Addable() {
           @Override
           public int add(int a, int b) {
               return a + b;
           }
       }));
   }
}
SAM Conversion
Kotlin
class MyActivity : Activity() {

   fun example() {
       val view = findViewById(R.id.button)
       view.setOnClickListener {
           Log.d("TAG", "Item clicked!")
       }
   }
}
Java
class MyActivity extends Activity {

   void example() {
       View view = findViewById(R.id.button);
       view.setOnClickListener(
           new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   Log.d("TAG", "Item clicked!");
               }
           }
       );
   }
}
COLLECTIONS
Using Collections
Kotlin
fun example() {
   val os = listOf("Android", "iOS", null,
                   "Windows Phone")

   os.filterNotNull().sortedBy { it.length }
           .map { it.toUpperCase() }
           .forEach { print(it) }

}
Java
class MyUtils {
  static void example() {
    List<String> os = Arrays.asList("Android",
                   "iOS", null, "Windows Phone");
    List<String> osNotNull = new ArrayList<>();
    for (String name : os) {
      if (name != null) osNotNull.add(name);
    }
    Collections.sort(osNotNull, new
      Comparator<String>() { @Override
         public int compare(String l, String r) {
           return l.length() - r.length();
         }
    });
    for (String name : osNotNull) {
       String value = name.toUpperCase();
       print(value);
    }
}
EXTESIONS
Extension: String
Kotlin
fun String.isBig(): Boolean {
   return length() > 10
}

fun example() {
   "why?!".isBig()                 // false!
   "P90, RUSH B, ...".isBig()   // true!
}
Java
// file MyUtils.java
class MyUtils {
   static boolean isBig(String str) {
       return str.length() > 10;
   }
}

// file MyJavaClass.java
class MyJavaClass {
   void example() {
       MyUtils.isBig("why?!");
       MyUtils.isBig("P90, RUSH B, ...");
   }
}
Simple reified Extension
Kotlin
inline fun <reified T : Any> Activity.service(): T {
   return HttpService.getApiService(T::class.java)
}

fun Activity.toast(str: String) {
   Toast.makeText(this, str,Toast.LENGTH_SHORT).show()
}


class MyActivity : Activity() {
	
   val baiduService = service()

   fun test() {
       toast("hahaha")
   }
}
Java
class MyActivity extends Activity {
   //leave out 
   Api baiduService = HttpService.getApiService(Api.class);

   void test() {
      ToastUtil.showToast(this, "hahaha");
   }
}
Extensions: Permission
Kotlin
inline fun Array.asPermissions(activity: Activity, fn: () -> Unit) {
   checkNotNull(activity)
   val PERMISSION_REQUEST_CODE = 11111
   var pred = true
   if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       this.filter { ActivityCompat.checkSelfPermission(activity, it) != PackageManager.PERMISSION_GRANTED }
               .forEach { pred = false }
   }
   if (pred) {
       fn()
   } else {
       ActivityCompat.requestPermissions(activity, this, PERMISSION_REQUEST_CODE)
   }
}

fun test(activity: Activity) {
   listOf(Manifest.permission.ACCESS_FINE_LOCATION,
           Manifest.permission.ACCESS_COARSE_LOCATION,
           Manifest.permission.READ_PHONE_STATE).asPermissions(activity) {
       // do things with required permissions
   }
}
Extensions: Make Phone Call
Kotlin
fun Activity.call(phone: String?) {
   phone?.let {
       if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) 
           == PackageManager.PERMISSION_GRANTED) {
           val callIntent = Intent(Intent.ACTION_DIAL)
           callIntent.data = Uri.parse("tel:" + it)
           this.startActivity(callIntent)
       } else {
           ActivityCompat.requestPermissions(this,
                   arrayOf(Manifest.permission.CALL_PHONE),
                   StaticConfig.RequestCode.REQUEST_PHONE)
           toast("请确认是否开启拨打电话权限")
       }
   }
   if (phone == null) {
       toast("没有电话!")
   }
}
DELEGATES
Lazy Delegation 1
Kotlin
class MyItem

class MyKotlinClass {
   val item by lazy { MyItem() }
}
Java
// in Kotlin is synchronized
class MyJavaClass {
   class MyItem { }

   MyItem item;

   final MyItem getItem() {
       if (item == null) {
           item = new MyItem();
       }
       return item;
   }
}
Lazy Delegation 2
Kotlin
class MyKotlinActivity : Activity() {
   val textView by lazy { findViewById(R.id.text) as TextView }
   val res by lazy { resources.getString(R.string.cancel) }
}
Java
class MyJavaActivity extends Activity {

   TextView textView;
   String res; 

   protected void onCreate(BundlesavedInstanceState) {
       super.onCreate(savedInstanceState);
       textView = (TextView) findViewById(R.id.text);
       res = getResources().getString(R.id.cancel)
   }
}
Butterknife
Kotlin
//usage
class MyActivity : Activity() {
   val someText by bindView<TextView>(R.id.text)
}

//implementation
private class Lazy<T, V>(private val initializer: (T, KProperty<*>) -> V) : ReadOnlyProperty<T, V> {
    private object EMPTY

    private var value: Any? = EMPTY

    override fun getValue(thisRef: T, property: KProperty<*>): V {
        if (value == EMPTY) {
  value = initializer(thisRef, property)
        }
        @Suppress("UNCHECKED_CAST")
        return value as V
    }
}

public fun <V : View> Activity.bindView(id: Int)
        : ReadOnlyProperty<Activity, V> = required(id, viewFinder)

private val Activity.viewFinder: Activity.(Int) -> View?
    get() = { findViewById(it) }

private fun <T, V : View> required(id: Int, finder: T.(Int) -> View?)
        = Lazy { t: T, desc -> t.finder(id) as V? ?: viewNotFound(id, desc) }
Java
//usage
class MyActivity extends Activity {
   @Bind(R.id.text)
   TextView someText;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       ButterKnife.bind(this);
   }
}

//implementation: 
//https://github.com/JakeWharton/butterknife
//tons of code
Observable
Kotlin
class User {
    var name: String by Delegates.observable("") {
        prop, old, new ->
        println("$old -> $new")
    }
}

fun example() {
    val user = User()
    user.name = "first"     // -> first
    user.name = "second"    //first -> second
}
Implement willSet and didSet
Kotlin
//usage
var x: Int by varWithObservableSetter(5)
       .willSet { println("before") }
       .didSet { println("after") }

var y: String by varWithObservableSetter("abc")
       .didSet { println("after") }


fun test() {
   x = 10      // before 5     // after 10
   y = "haha"  //after "haha"
}

//implementation
class VarWithObservableSetter<T,
       out TWillSet : (() -> Unit)?,
       out TDidSet : (() -> Unit)?>
internal constructor(
       val initialValue: T,
       internal val wilLSet: TWillSet,
       internal val didSet: TDidSet
) : ObservableProperty<T>(initialValue) {
   override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean {
       wilLSet?.invoke()
       return true
   }

   override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) {
       didSet?.invoke()
   }
}

fun <T> varWithObservableSetter(initialValue: T) =
       VarWithObservableSetter(initialValue, null, null)

fun <T, R : (() -> Unit)?>
       VarWithObservableSetter<T, Nothing?, R>.willSet(action: () -> Unit) =
       VarWithObservableSetter(initialValue, action, didSet)

fun <T, R : (() -> Unit)?>
       VarWithObservableSetter<T, R, Nothing?>.didSet(action: () -> Unit) =
       VarWithObservableSetter(initialValue, wilLSet, action)

SMART CAST AND PATTERN MATCHING
Kotlin
fun example1(myView: View) {
   if (myView is ImageView) {
       myView.setImageAlpha(10)
   } else if (myView is TextView) {
       myView.setText("Ciao")
   }
}
//or
fun example2(myView: View) {
   when (myView) {
       is ImageView -> myView.imageAlpha = 10
       is TextView -> myView.text = "Ciao"
   }
}
Java
class MyUtils {

   static void example(View myView) {
       if (myView instanceof ImageView) {
           ImageView imageView =
               (ImageView) myView;
           imageView.setImageAlpha(10);
       } else if (myView instanceof TextView) {
           TextView textView = (TextView) myView;
           textView.setText("Ciao");
       }
   }
}
STANDARD LIBRARY
let
Kotlin
customer?.let {
    doSth(it.id)
}
Java
if (customer != null) {
    doSth(customer.id)
}
Source Code
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
apply
Kotlin
File(dir).apply { mkdirs() }
Java
File makeDir(String path) {
   File result = new File(path);
   result.mkdirs();
   return result;
}
Source Code
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
with
Kotlin
fun Activity.toast(str: String) {
    Toast.makeText(this, str, Toast.LENGTH_SHORT).show()
}

visitList.setOnClickListener {
    with(context as Activity) {
        toast("haha")
    }
}
Source Code
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
DSL
Bundle
Kotlin
fun bundle(vararg pair: Pair<String, Any>): Bundle {
    val bundle = Bundle()
    for ((k, v) in pair) {
        when (v) {
           is String -> this.putString(k, v)
           is Int -> this.putInt(k, v)
           is Serializable -> this.putSerializable(k, v)
           is Bundle -> this.putBundle(k, v)
           is Long -> this.putLong(k, v)
           ...
        }
    }
    return bundle
}

inline fun <reified T : Activity> Activity.jump(requestCode: Int, extras: Bundle) =
       ActivityUtils.jump(this, T::class.java, requestCode, extras)


fun example() {
    jump<MainActivity>(SOME_CODE, bundle("int-key" to 1, "string-key" to 2))
}
Converter
Kotlin
//usage
fun testDSL() {
   convert("ABC") {
       toLower()    //ABC => abc
       toUpper()    //abc => ABC
   }
   convert(2.2) {
       toLower()    //2.2 => 2.0
   }
   convert(2) {
       toLower()    //编译器:没有这个方法
   }
}

//implementation
class MConverter<T>(var value: T) {
   val old = value
   override fun toString(): String = 
      	     "[$old] => [$value]"
}

fun <T> convert(
   value: T,
   init: MConverter<T>.() -> Unit): MConverter<T> {
   val converter = MConverter(value)
   converter.init()
   return converter
}

fun MConverter<String>.toLower() {
   value = value.toLowerCase()
}

fun MConverter<String>.toUpper() {
   value = value.toUpperCase()
}

fun MConverter<Double>.toLower() {
   value = Math.floor(value)
}
Retrofit
Kotlin
//usage
someService.someApi(param1, param2).enqueue {
   onResponse {
      toast("succeed")
   }
   onFailure { toast("failed") }
   onFinish { hideLoading() }
}

//implementation
inline fun <T> Call<T>.enqueue(init: CallbackHelper<T>.() -> Unit) {
   val listener = CallbackHelper<T>()
   listener.init()
   enqueue(listener)
}

class CallbackHelper<T> : Callback<T> {

   private var _onResponse: ((T) -> Unit)? = null
   private var _onFailure: ((Throwable) -> Unit)? = null
   private var _onFinish: (() -> Unit)? = null

   override fun onResponse(call: Call<T>, response: Response<T>) {
       onResponse?.invoke(response.body())
       _onFinish?.invoke()
   }

   fun onResponse(listener: (T) -> Unit) {
       _onResponse = listener
   }

   fun onFinish(listener: () -> Unit) {
       _onFinish = listener
   } 

   override fun onFailure(call: Call<T>, t: Throwable) {
       _onFailure?.invoke(t)
       _onFinish?.invoke()
   }

   fun onFailure(listener: (Throwable) -> Unit) {
       _onFailure = listener
   }
}
QUIZ
step 1: solve this quiz. step 2: ???. step 3: profit.
Kotlin
class SomeHolder(var x: Int, val action: () -> Unit)

val sadHolder = SomeHolder(1) { print("I want to print x, but i don’t know how.") }

//modify the following line to let the magic happen
val happyHolder = SomeHolder(1) { print("I know what x is, it's $x")}