2017年6月20日 星期二

[Kotlin] 學習筆記(2) - Basic Concept

在開始之前,附上一篇文章 17位Google Android 工程師對 Kotlin 的看法

來了解一下各方對 Kotlin 的見解

然後也提供一下學習資源 : Kotlin官方文檔

本篇大部分內容都是參考 Kotlin 的官方文檔來的,可以去該網站多挖挖寶

以下開始一些基礎介紹
  • Variables
有兩個關鍵字,val 和 var。val 就是 java 裡的 final,var 是可以重新賦值的變數

val number: Int = 1 //>> same as "final int number = 1;" in Java
val number = 1 //>> Kotlin will infer type of number is Int。similar to "auto number = 1;" in C++
val number: Int //>> if no initial value, type must be given
number = 3
var 也有上面的宣告方式,差別在於 var 可以重新賦值

在 Kotlin,變數預設是 non-null type,也就是不能是 null

Kotlin 的一大特色就是能盡量減少 NullPointerException 發生的機率

以下是關於變數 null safety 的介紹
var a: String = null //>> a is non-null type, compilation error
var b: String? = null //>> b is nullable, okay
val lengthA = a.length //>> okay because a is non-null type
val lengthB = b.length //>> error because b might be null. see following usage
//>> Usage 1:
//>> use safe call operator, written as ?.
//>> b?.length return b.length if b is not null and null otherwise
//>> the type of lengthB is Int?
val lengthB = b?.length
//>> Usage 2:
//>> let lengthB has default value when b is null and type of lengthB is Int instead of Int?
val lengthB: Int = if (b != null) b.length else -1 //>> method 1
val lengthB = b?.length ?: -1 //>> method 2, use Elvis operator, written as ?:
//>> Usage 3:
//>> use !! operator if you want NullPointerException
//>> there will be a NullPointerException when b is null
val lengthB = b!!.length
型態轉換也有 null safety 可以使用
//>> the as? will return null if the cast was not successful
val newInt: Int? = someVar as? Int
如果今天有一個 class 的 property,我們不希望為 null,但也不想在 construtor 去初始化,該怎麼做呢?
class MyKotlinClass {
private var mMsg: String //>> compilation error. non-null type need initial value
fun initMsg() {
mMsg = "init"
}
}
//>> use lateinit keyword
class MyKotlinClass {
private lateinit var mMsg: String
fun initMsg() {
mMsg = "init"
}
}
lateinit 的用法有一些限制
(1) 只能用在 var 變數
(2) 不能用在 primitive type (Int, Float, ...) 
(3) 該變數不能有客製化的 setter 和 getter
  • Functions
有兩個 Int 的 parameters,回傳 type 是 Int
fun getSum(a: Int, b: Int): Int {
return a+b
}
//>> can be simplified further
//>> Kotlin will infer return type
fun getSum(a: Int, b: Int) = a + b
如果沒有 return value,用 Unit 表示,也可以把 Unit 省略
//>> no return value
fun showMsg(msg: String): Unit {
println(msg)
}
//>> omit Unit
fun showMsg(msg: String) {
println(msg)
}
如果一個函式的參數比較多,這時候可以使用 named arguments 來增加可讀性
fun doAction(msg: String, isTestMode: Boolean = false, ver: Int = 1) {
//>> function body
}
//>> use named arguments
doAction("Hello Kotlin", ver = 3, isTestMode = true)
function 也可以使用 variable number of arguments

使用 vararg 有幾點注意事項
  1. function 只能有一個 parameter 被標示為 vararg
  2. vararg parameter 通常要放在 parameter list 的最後一個。如果不是最後一個,則在使用的時候,必須搭配 named argument 來使用
//>> function with variable number arguments
fun <T> toList(vararg input: T) : List<T> {
val result = ArrayList<T>()
input.forEach { n -> result.add(n) }
return result
}
//>> example 1
val list = toList(1, 2, 3, -1, -2, -3)
//>> example2
//>> use * to spread array a
val a = arrayOf(1, 2, 3)
val b = toList(7, 5, *a, -5)
//==========================================================
//>> vararg is not the last one in the list
fun <T> toList(vararg input: T, ver: Int ) : List<T> {
val result = ArrayList<T>()
input.forEach { n -> result.add(n) }
return result
}
//>> need to use named argument syntax to pass following parameter
val list = myKotlinClass.toList(1, 2, 3, ver = 111)
  • Class
在 Kotlin,如果 class 沒有指明父類別的話,都會是從 Any 這個例別衍生出來的

class 的基本宣告方式如下
//>> this class cannot be inherited
class MyClass {
//>> class body
}
//>> this class can be inherited
open class MyClass {
//>> class body
}
Kotlin 有個和 Java 不一樣的地方是,Kotlin 沒有 new 這個 keyword
//>> create instance of class
val myClass = MyClass() //>> there is no 'new' keyword in Kotlin
每個 class 都會有一個 primary constructor
//>> this class will have auto-generated constructor with no arguments
//>> the auto-generated constructor's visibility will be public
class MyClass {
//>> class body
}
//>> this class has a public primary constructor with one argument
class MyClass constructor(msg: String) {
//>> class body
}
//>> you can omit the constructor keyword when primary constructor has no
//>> annotations or visiblity modifiers
class MyClass(msg: String) {
//>> class body
}
//>> want a private primary constructor
class MyClass private constructor(msg: String) {
//>> class body
}
//>> constructor example 1
class MyKotlinClass(msg: String, ver: Int) {
private var privateMsg = "[private]" + msg
private var privateVer = ver + 20
fun setup() {
Log.d("TAG", "private message is $privateMsg, private version is $privateVer")
}
}
//>> constructor example 2
class MyClass(val msg: String, var ver: Int) {
private var privateMsg = "[private]" + msg
fun setup() {
Log.d("TAG", "user's message is $msg, current version is $ver")
ver = -1 //>> change the value
Log.d("TAG", "private message is $privateMsg, modified version is $ver")
}
}
usage:
val myKotlinClass = MyKotlinClass("Hi Kotlin", 33)
myKotlinClass.setup()
=> outputs are:
user's message is Hi Kotlin, current version is 33
private message is [private]Hi Kotlin, modified version is -1
primary constructor 是不能寫 code 的,必須使用 initializer block 來達到這個目的
//>> use initializer block to contain code for primary constructor
class MyClass(msg: String) {
init {
Log.d("TAG", "user's message is $msg")
}
}
Kotlin 還有提供 secondary constructor,一個 class 可以有多個 secondary constructor
//>> this class has no customized primary constructor
//>> and has one secondary constructor
class MyClass {
constructor(secondMsg: String) {
println("second message is ${secondMsg}")
}
}
//>> if the class has a primary constructor
//>> then each secondary constructor need to delegate to the primary constructor
class MyClass(msg: String) {
constructor(msg:String, number: Int) : this(msg){
println("[$number] message is $msg")
}
constructor(msg: String, number: Int, ver: Double) : this(msg) {
println("[$number] message is $msg and version is $ver")
}
}
在 Kotlin,function 和 constructor 的 arguments 都可以有 default value,這可以簡化原本 Java 的寫法
//>> Java Style
public class MyJavaClass {
public MyJavaClass(String msg, int number) {
this(msg, number, 1.8);
}
public MyJavaClass(String msg, int number, double ver) {
Log.d("MyJavaClass", "[" + number + "] message is " + msg + ", and version is " + ver);
}
}
=> val myJavaClass = MyJavaClass("Hi Java", 23)
output: [23] message is Hi Java, and version is 1.8
=> val myJavaClass = MyJavaClass("Hi Java", 23, 6.6)
output: [23] message is Hi Java, and version is 6.6
//>> Kotlin Style
class MyKotlinClass {
constructor(msg: String, number:Int, ver: Double = 1.8) {
Log.d("MyKotlinClass", "[$number] message is $msg, and version is $ver")
}
}
=> val myKotlinClass = MyKotlinClass("Hi Kotlin", 33)
output: [33] message is Hi Kotlin, and version is 1.8
=> val myJavaClass = MyJavaClass("Hi Kotlin", 33, 10.2)
output: [33] message is Hi Kotlin, and version is 10.2
在繼承 class 的時候,constructor 的寫法有下列兩種情況
//>> if the derived class has primary constructor
//>> the base class must be initialized right here
open class Base(msg: String) {
//>> class body
}
class Derived(msg: String) : Base(msg) {
//>> class body
}
//>> if the derived class has no primary constructor
//>> each secondary constructor must use super to initialize base class
open class Base(msg: String) {
//>> class body
}
class Derived : Base {
constructor(msg: String, ver: Int) : super(msg)
}
以下示範 derived class 如何複寫 function
open class Base {
fun doAction() {
//>> function body
}
open fun showMsg() {
//>> function body
}
}
class Derived : Base() {
//>> this is a compilation error
//>> derived class can only override open function
override fun doAction() {
//>> function body
}
//>> okay
override fun showMsg() {
//>> function body
}
}
當 derived class 繼承或實現多個 base class or interface 的情況呢
open class BaseClass {
open fun doAction() {
println("don't do it")
}
open fun showMsg() {
println("Hi Kotlin")
}
}
interface BaseInterface {
//>> interface members are 'open' by default
fun doAction() {
println("just do it")
}
fun power() {
println("interface only")
}
}
class Derived : BaseClass(), BaseInterface {
//>> doAction() must be override because both BaseClass and BaseInterface have doAction()
override fun doAction() {
super<MyKotlinClass>.doAction() //>> call to BaseClass.doAction()
super<BaseInterface>.doAction() //>> call to BaseInterface.doAction()
}
}
可以看到因為有 doAction() 的 supertype 不只一個,所以在 Derived class 就一定得用 override 去複寫這個函式

而在這種情況下,要指定使用哪個 supertype 的 doAction(),就可以使用 super<BaseName> 來指定

至於 showMsg() 和 power(),因為來源都只有一個,所以不會被強迫要求用 override 複寫

  • What's Next
本篇介紹一些 Kotlin 基礎的語法,可以看出來 Kotlin 有簡化一些 Java 語法,並且引入一些新的概念

不過也可以看出來,Kotlin 在某些部分也有一些限制,像是為了 null safety,programmer 必須在寫 code 的時候遵從一些語法才行

下一篇會繼續介紹 Kotlin 的基礎語法,讓各位更熟悉 Kotlin

2017年6月16日 星期五

[LeetCode] 523. Continuous Subarray Sum

轉自LeetCode

Given a list of non-negative numbers and a target integer k, write a function to check if the array has a continuous subarray of size at least 2 that sums up to the multiple of k, that is, sums up to n*k where n is also an integer.
Example 1:
Input: [23, 2, 4, 6, 7],  k=6
Output: True
Explanation: Because [2, 4] is a continuous subarray of size 2 and sums up to 6.
Example 2:
Input: [23, 2, 6, 4, 7],  k=6
Output: True
Explanation: Because [23, 2, 6, 4, 7] is an continuous subarray of size 5 and sums up to 42.
Note:
  1. The length of the array won't exceed 10,000.
  2. You may assume the sum of all the numbers is in the range of a signed 32-bit integer.
<Solution>

2017年6月15日 星期四

[LeetCode] 560. Subarray Sum Equals K

轉自LeetCode

Given an array of integers and an integer k, you need to find the total number of continuous subarrays whose sum equals to k.
Example 1:
Input:nums = [1,1,1], k = 2
Output: 2
Note:
  1. The length of the array is in range [1, 20,000].
  2. The range of numbers in the array is [-1000, 1000] and the range of the integer k is [-1e7, 1e7].
<Solution>

2017年6月12日 星期一

[Kotlin] 學習筆記 (1) - Hello Kotlin

在 Google I/O 2017 的大會上

Google 正式宣布 Android Studio 3.0 將會正式支援 Kotlin

這也讓 Kotlin 成為目前熱烈討論的程式語言之一

和 Java 一樣,Kotlin 也是一個 JVM 的語言,由 JetBrains 所開發

因此,Kotlin 和 Java 是可以同時並存一起使用的

至於 Kotlin 會不會取代 Java,以及 Kotlin 和 Java 誰比較好的問題

在這邊就不多作討論,因為這是滿主觀的問題

所以在這邊主要會介紹,怎麼運用 Kotlin 在 Android APP 的開發上

  • 安裝 Kotlin plugin
因為目前 Android Studio 3.0 還沒有正式釋出,所以目前還是得透過安裝 plugin 的方式來使用 Kotlin

到 Preference 下的 plugin 頁面來安裝 Kotlin plugin




  • Hello Kotlin App
接下來寫一個簡單的 App

先看原本 Java 的寫法

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.textView);
textView.setText("Java Style");
}
}


接下來看怎麼用 Kotlin 改寫

首先,先設置好 Kotlin,按下 cmd + shift + a,搜尋 kotlin,選擇 configure kotlin in project


Kotlin 寫法

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView = findViewById(R.id.textView) as TextView
textView.text = "Kotlin Style"
}
}


<ps> 在一開始不知道怎麼寫 kotlin 的時候,也可以用 cmd + shift + a -> Convert Java File to Kotlin File 來看怎麼寫

整體來看,好像沒有差別很大,也沒有變得比較簡潔

比較有差別的地方是,可以不用使用 setText(),而變成直接用 property 的方式來設定 text

其實 Kotlin 還有更簡潔的寫法(資料來源)

在 module 的 build.grandle 裡面,加入下面這段 code

apply plugin: 'kotlin-android-extensions'

這時候可以進一步改寫成


不用再使用 findViewById 來找到 TextView,可以直接使用 ID 來存取該 TextView

  • 下一步
這次先簡單介紹一下 Kotlin,之後會再陸續介紹 Kotlin 的各種語法