val thread = Thread { for (i in 10 downTo 1) { try { sleep(1000) } catch (e: InterruptedException) { e.printStackTrace() return@Thread } runOnUiThread { binding.tvShow.text = "count down $i ..." } } runOnUiThread { binding.tvShow.text = "Done!" } } thread.start() binding.btnTest.setOnClickListener { thread.interrupt() }
下面使用CoroutineScope功能與上方相同
//GlobalScope 繼承 CoroutineScope //全域Scope,整個應用程式的生命週期,最頂級的 //使用他相當於使用靜態函數 //可以避免被提早結束 val job: Job = GlobalScope.launch(Dispatchers.Main) { for (i in 10 downTo 1) { binding.tvShow.text = "count down $i ..." // update text delay(1000) } binding.tvShow.text = "Done!" } binding.btnTest.setOnClickListener { job.cancel() }
2. 繼承CoroutineScope使用job
class MainActivity : AppCompatActivity(), CoroutineScope {
//job沒有特別定義的話
//launch,會跑在非MainThread,不可刷新UI
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job
private fun runTimer() {
launch {
for (i in 10 downTo 1) {
//不能刷新UI會閃退
//binding.tvShow.text = "count down $i ..." // update text
delay(1000)
}
//不能刷新UI會閃退
//binding.tvShow.text = "Done!"
}
binding.btnTest.setOnClickListener {
job.cancel()
}
}
override fun onDestroy() {
super.onDestroy()
//只有取消這個job
job.cancel()
}
}
3. CoroutineScope委託MainScope
//CoroutineScope委託MainScope
//此時launch會跑在MainThread
class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
override fun onDestroy() {
super.onDestroy()
//Scope cancel意味著全部取消
cancel()
}
private fun runTimer() {
val job = launch {
for (i in 10 downTo 1) {
binding.tvShow.text = "count down $i ..." // update text
delay(1000)
}
binding.tvShow.text = "Done!"
}
binding.btnTest.setOnClickListener {
//job cancel意味著只取消這個工作
job.cancel()
}
}
}
4. CoroutineScope搭配suspend
//CoroutineScope委託MainScope
//此時launch會跑在MainThread
class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
override fun onDestroy() {
super.onDestroy()
//Scope cancel意味著全部取消
cancel()
}
//suspend讓自己原先的執行緒暫停
//切換到Dispatchers.IO
//處理完後,再回來原先的執行緒繼續往下作
suspend fun readSomeFile() = withContext(Dispatchers.IO) {
//模擬處理一些資料
var j = 0
for(i in 0 until 1000000000) {
j += 1
}
}
private fun runTimer() {
val job = launch(Dispatchers.Main) {
binding.tvShow.text = "123"
binding.tvShow.visibility = View.VISIBLE
readSomeFile()
binding.tvShow.visibility = View.GONE
}
binding.btnTest.setOnClickListener {
//job cancel意味著只取消這個工作
job.cancel()
}
}
}
5. CoroutineScope+async+await
以下會打印result2
class MainActivity : AppCompatActivity(), CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job
override fun onCreate(savedInstanceState: Bundle?) {
val rawText: Deferred<String> = async(Dispatchers.IO) {
"result2"
}
launch {
//需放在此使用
val text = rawText.await()
println(text)
}
//在此用會error
//rawText.await()
}
override fun onDestroy() {
super.onDestroy()
//Scope cancel意味著全部取消
job.cancel()
}
}
6. runBlocking+async+suspend
以下會打印
The answer is 42 Completed in 1049 ms
private fun testFunction() = runBlocking { val time = measureTimeMillis { val one = async { doSomethingUsefulOne() } val two = async { doSomethingUsefulTwo() } println("The answer is ${one.await() + two.await()}") } println("Completed in $time ms") } suspend fun doSomethingUsefulOne(): Int { delay(1000L) return 13 }
suspend fun doSomethingUsefulTwo(): Int { delay(1000L) return 29 }
class Example {
var p: String by Delegate()
}
class Delegate {
//重寫get
operator fun getValue(
thisRef: Any?,
property: KProperty
): String {
return "$thisRef, thank you for delegating " +
"${property.name} to me!"
}
//重寫set
operator fun setValue(
thisRef: Any?,
property: KProperty,
value: String
) {
println("$value has been assigned to " +
"${property.name} in $thisRef.")
}
}
@Test
fun test() {
val e = Example()
//打印com.xxxx.ExampleUnitTest$Example@157632c9,
//thank you for delegating 'p' to me!
println(e.p)
//打印123 has been assigned to 'p' in
//com.xxxx.ExampleUnitTest$Example@157632c9.
e.p = "123"
}
2. lazy懶加載用法+String.split
private val data: String by lazy {
println("init data")
"init data"
}
@Test
fun test() {
//呼叫這行時,打印 init data
val array = data.split(" ")
//呼叫這行時打印 array = [init, data], array.size 2
println("array = $array, array.size ${array.size}")
}
3. 可觀察屬性observable
//可觀察屬性observable
class User {
//初始值是
var dataName: String by Delegates
.observable("") {
prop, old, new ->
println("${prop.name} $old -> $new")
}
}
@Test
fun test() {
val user = User()
user.dataName = "first"
//打印dataName -> first
user.dataName = "second"
//打印dataName first -> second
}
4. 委託內建map
//唯讀的map
class User(
map: Map
) {
val name: String by map
val age: Int by map
}
//可讀可寫
class MutableUser(
map: MutableMap
) {
var name: String by map
var age: Int by map
}
private val user = User(
mapOf(
"name" to "John Doe",
"age" to 25
)
)
private val mutableUser = MutableUser(
mutableMapOf(
"name" to "John Doe",
"age" to 25
)
)
@Test
fun test() {
println(user.name)
// 打印 John Doe
println(user.age)
// 打印 25
println(mutableUser.name)
// 打印 John Doe
println(mutableUser.age)
// 打印 25
}
5. 本地委託屬性,此方法可以減少加載
interface I {
fun doSomething()
fun isValid(): Boolean
}
private fun example(
someCondition: Boolean,
computeFoo: () -> I
) {
//當someCondition == true,才會加載computeFoo
val memoizedFoo by lazy(computeFoo)
if (someCondition && memoizedFoo.isValid()) {
memoizedFoo.doSomething()
}
}
private val callback = object : I {
override fun doSomething() {
println("doSomething")
}
override fun isValid(): Boolean {
return false
}
}
@Test
fun test() {
example(true) {
println("process data")
callback
}
}
6. 委託屬性,唯讀與可讀可寫的方法
fun resourceDelegate(): ReadWriteProperty =
object : ReadWriteProperty {
var curValue = 0
override fun getValue(
thisRef: Any?,
property: KProperty
): Int = curValue
override fun setValue(
thisRef: Any?,
property: KProperty,
value: Int
) {
curValue = value
}
}
val readOnly: Int by resourceDelegate()
// ReadWriteProperty as val
var readWrite: Int by resourceDelegate()
@Test
fun test() {
readOnly = 0//錯誤
readWrite = 0//正常
}
7. 將舊方法棄用
class MyClass {
//將舊的參數棄用方式
var newName: Int = 0
@Deprecated("Use 'newName' instead",
ReplaceWith("newName")
)
var oldName: Int by this::newName
}
@Test
fun test() {
val myClass = MyClass()
// Notification: 'oldName: Int' is deprecated.
// Use 'newName' instead
myClass.oldName = 42
println(myClass.newName) // 42
}
fun MutableList.swap(index1: Int, index2: Int) {
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
fun MutableList.swap2(index1: Int, index2: Int) {
//用Android Kotlin 基本語法3那篇的,快速兩資料交換的方法
this[index1] = this[index2].also {
this[index2] = this[index1]
}
}
@Test
fun test() {
val list = mutableListOf(10, 20, 30)
list.swap(0, 2)
list.forEach {
println("A = $it")
}
/*
打印
A = 30
A = 20
A = 10*/
list.swap2(0, 2)
list.forEach {
println("B = $it")
}
/*
打印
B = 10
B = 20
B = 30*/
}
2. 擴充外加函式,幫助程式簡化
class Host(val hostname: String) {
fun printHostname() { print(hostname) }
}
class Connection(val host: Host, val port: Int) {
fun printPort() {
print(port)
}
//利用此方法幫助程式簡化
fun Host.printConnectionString() {
printHostname()
print(":")
printPort()
}
fun connect() {
host.printConnectionString()
}
}
@Test
fun test() {
Connection(Host("kotl.in"), 443).connect()
}
3. T,多類型引入
class Box(t: T) {
var value = t
}
@Test
fun test() {
val box = Box("123")
}
4. T搭配in、out使用
//T搭配out用法
interface Source {
//in -> 引入T, out -> return T, 此處為"return T"
fun nextT(): T
}
fun demo(callback: Source) {
val objects: Source = callback
println(objects.nextT())//打印"hello"
}
val sourceCallback = object : Source{
override fun nextT(): String {
return "hello"
}
}
@Test
fun test() {
demo(sourceCallback)
}
//T搭配in用法
interface Comparable {
//in -> 引入T, out -> return T, 此處為"引入T"
fun compareTo(data1: T, data2: T): Boolean
}
fun demo(x: Comparable) {
//打印false
println(x.compareTo(1.0, 2.0))
// Double是Number的子類型
//因此可以這樣賦予
val y: Comparable = x // OK!
}
@Test
fun test2() {
demo(object : Comparable{
override fun compareTo(
data1: Number,
data2: Number
): Boolean = data1 == data2
})
}
5. 1個對象的object用法
class C {
// 私有函數內,返回的函數也是私有的
private fun foo() = object {
val x: String = "x"
}
// 公有函數,內容也是公有
fun publicFoo() = object {
val x: String = "x"
}
fun bar() {
val x1 = foo().x
// private fun 正常
val x2 = publicFoo().x
// fun 錯誤
}
}
6. 類型別名
//typealias 是重新將底層類型命名
//testString is String
typealias testString = String
class ExampleUnitTest {
@Test
fun test() {
val data: testString = "1234"
println(data)
//print 1234
}
}
7. 內聯類,為了減少大量程式碼的開銷
//inline在1.5版以後,不推薦使用的內聯類
//應改為下方
@JvmInline
value class Password(val value: String)
@Test
fun test() {
//securePassword運行的時候,內部會簡化成只代表String
val securePassword =
Password("Don't try this in production")
println(securePassword)
}
var counter = 0
set(value) {
if (value >= 0)
field = value
//field 就是backing fields
}
//還有支持一些基本用法
//但有一些限制,不能含有 backing fields
//只能做簡單的計算(沒有lateinit/delegated屬性)
@JvmInline
value class Name(val s: String) {
init {
require(s.length > 0) {
//require <= 0
//throw java.lang.IllegalArgumentException
}
}
val length: Int
get() = s.length
fun greet() {
println("Hello, $s")
}
}
@Test
fun test() {
val name = Name("Kotlin")
name.greet()
// `greet` 方法會作為一個靜態方法調用
println(name.length)
//屬性的get方法會作為一個靜態方法調用
}
//內聯類允許繼承
interface Printable {
fun prettyPrint(): String
}
@JvmInline
value class Name(val s: String) : Printable {
override fun prettyPrint(): String = "Let's $s!"
}
@Test
fun test() {
val name = Name("Kotlin")
println(name.prettyPrint())
// Still called as a static method
}
//typealias 是重新將底層類型命名
//@JvmInline
// value是生成新的類型
typealias NameTypeAlias = String
@JvmInline
value class NameInlineClass(val s: String)
fun acceptString(s: String) {}
fun acceptNameTypeAlias(n: NameTypeAlias) {}
fun acceptNameInlineClass(p: NameInlineClass) {}
@Test
fun test() {
val nameAlias: NameTypeAlias = ""
val nameInlineClass: NameInlineClass =
NameInlineClass("")
val string: String = ""
acceptString(nameAlias)
//正常,因為typealias 是重新將底層類型命名
acceptString(nameInlineClass)
//錯誤,內聯是新建新的類型,所以不等於String
// And vice versa:
acceptNameTypeAlias(string)
//正常,因為typealias 是重新將底層類型命名
acceptNameInlineClass(string)
//錯誤,內聯是新建新的類型,所以不等於String
}
8. 委託(Delegation)
interface Base {
val message: String
fun print()
}
class BaseImpl(x: Int) : Base {
override val message = "BaseImpl: x = $x"
override fun print() { println(message) }
}
class Derived(b: Base) : Base by b {
//by b -> 如果此處有override,以此處為優先
//若沒有override,則使用Base內預設的.
override val message = "Message of Derived"
}
@Test
fun test() {
val b = BaseImpl(10)
val derived = Derived(b)
derived.print()
println(derived.message)
/*
BaseImpl: x = 10
Message of Derived
*/
}
class InitOrderDemo(name: String) {
val firstProperty = "First p: $name"
.also(::println)
init {
println("First init test ${name}")
}
val secondProperty = "Second p: ${name.length}"
.also(::println)
init {
println("Second init test ${name.length}")
}
}
@Test
fun test() {
InitOrderDemo("123")
/*
First p: 123
First init test 123
Second p: 3
Second init test 3
*/
}
open class Shape {
open fun draw() {
println("Shape draw")
}
fun fill() { /*...*/ }
}
//因為Circle沒有open,所以不能繼承
class Circle() : Shape() {
override fun draw() {
super.draw()
println("Circle draw")
}
}
open class Rectangle() : Shape() {
final override fun draw() {
super.draw()
println("Rectangle draw")
}
}
open class Test2() : Rectangle() {
//因為Rectangle的draw是final所以禁止override
}
4. 不同的寫法,影響override能不能改變值
interface Shape {
val vertexCount: Int
}
class Rectangle(
override val vertexCount: Int = 4
) : Shape
// Always has 4 vertices
class Polygon : Shape {
override val vertexCount: Int = 0
}
@Test
fun test() {
Rectangle(5)
Polygon()//不能改變vertexCount值
}
5. 繼承與當前Class執行順序
open class Base(val name: String) {
init {
println("Init a base class")
}
open val size: Int = name.length.also {
println("Init size: $it")
}
}
class Derived(
name: String,
val lastName: String,
) : Base(
name.replaceFirstChar {
it.uppercase()
}.also {
println("Argument for the base class: $it")
}
) {
init {
println("Init a derived class")
}
override val size: Int =
(super.size + lastName.length)
.also {
println("Init size: $it")
}
}
@Test
fun test() {
Derived("apple", "windows")
/*
Argument for the base class: Apple
Init a base class
Init size: 5
Init a derived class
Init size: 12
*/
}
6. 由inner class去找parent的super
open class Rectangle {
open fun draw() {
println("Drawing a rectangle")
}
val borderColor: String get() = "black"
}
class FilledRectangle: Rectangle() {
override fun draw() {
val filler = Filler()
filler.drawAndFill()
}
inner class Filler {
fun fill() {
println("Filling")
}
fun drawAndFill() {
super@FilledRectangle.draw()
fill()
println("color " +
super@FilledRectangle.borderColor
)
}
}
}
@Test
fun test() {
FilledRectangle().draw()
/*
Drawing a rectangle
Filling
color black
*/
}
7. 繼承多個class時,選擇指定的Parent
open class Rectangle {
open fun draw() {
println("Rectangle draw")
}
}
interface Polygon {
fun draw() {
println("Polygon draw")
} // interface members are 'open' by default
}
class Square() : Rectangle(), Polygon {
// The compiler requires draw() to be overridden:
override fun draw() {
super.draw() // call to Rectangle.draw()
super.draw() // call to Polygon.draw()
}
}
@Test
fun test() {
Square().draw()
/*
Rectangle draw
Polygon draw
*/
}
8. Private protected public internal修飾符差異
//private:只能在內部看到
//protected:在內部看得到,繼承後也看得到
//internal:只能在相同模塊看到
//public: 任何客戶端都可以見到
open class Outer {
private val a = 1
protected open val b = 2
internal val c = 3
val d = 4 // 都沒設定預設 public
protected class Nested {
public val e: Int = 5
}
}
class Subclass : Outer() {
// a 不可見
// b、c、d 可見
// Nested 和 e 可見
override val b = 5 // 繼承父,所以一樣是protected
}
class Unrelated(val o: Outer) {
// o.a、o.b 不可見
// o.c 和 o.d 可見(相同模塊)
// Outer.Nested 不可見,Nested::e 也不可見
}
internal open class Example {
class Test1: Example() {//相同模塊所以可以繼承
}
inner class Test2: Example() {//相同模塊所以可以繼承
}
}
class Test3: Example() {//不同模塊所以不能繼承
}
private fun String.clearSpace(): String = replace(" ", "")
@Test
fun test() {
val string = "A B C D E F G".clearSpace()
println("string = $string")//string = ABCDEFG
}
2. Abstract技巧使用
abstract class MyAbstractClass {
var value: Int = -1
set(value) {
if(value > 0) {
field = value
success()
} else {
field = -1
error()
}
}
abstract fun success()
abstract fun error()
}
@Test
fun test() {
val myObject = object : MyAbstractClass() {
override fun success() {
println("success")
}
override fun error() {
println("error")
}
}
myObject.value = -1//打印error
myObject.value = 1//打印success
}
3. throw使用方法
fun setValue(data: Any?) {
if(data is Int) {
println("data is Int: $data")
} else {
throw NumberFormatException("data is not Int")
}
}
@Test
fun test() {
val data = "data String"
var result = false
try {
println("try")//1.先打印此
setValue(data)
result = true
} catch (e: NumberFormatException) {
println("catch")//2.再來此
e.printStackTrace()
} finally {
println("finally")//3.再來此
//此處無論是經過try還是catch,之後都會先經過這
}
println("result $result")//4.result false
}
4. 利用let特性處理資料型態?
var data: String? = "Apple"
var data2: String? = null
@Test
fun test() {
data = data?.let {
"transform $data"
} ?: "error A"
data2 = data2?.let {
"transform $data"
} ?: "error B"
println("data = $data, data2 = $data2")
//data = transform Apple, data2 = error B
}
@Test
fun test() {
var a = 1
var b = 2
a = b.also {
b = a
}
println("a = $a, b = $b")//a = 2, b = 1
}
7. 自定義待處理的fun
fun calcTaxes(): BigDecimal = TODO("test TODO")
@Test
fun test() {
//會拋出下方
//kotlin.NotImplementedError: An operation is not
//implemented: test TODO
calcTaxes()
}
8. 設定return標籤
@Test
fun test() {
loop@ for (i in 1..5) {
for (j in 1..5) {
if (i == 2 && j == 2) break@loop
println("i = $i, j = $j")
}
}
/*
i = 1, j = 1
i = 1, j = 2
i = 1, j = 3
i = 1, j = 4
i = 1, j = 5
i = 2, j = 1
*/
}
@Test
fun test2() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return
// non-local return directly to the caller of foo()
print(it)
}
println("this point is unreachable")
//打印12
}
@Test
fun test3() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit
print(it)
}
println(" done with explicit label")
//打印1245 done with explicit label
}
@Test
fun test4() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach
print(it)
}
println(" done with implicit label")
//打印1245 done with explicit label
}
class InfoClass(
private var name: String,
private var number: Int = 1
) {
fun getInfo() {
println("name $name, number $number")
}
}
@Test
fun test2() {
val test = InfoClass("Apple")
test.getInfo() //打印 name Apple, number 1
val test2 = InfoClass("John", 30)
test2.getInfo() //打印 name John, number 30
}
2. 內部class用法
class MainClass {
var data = 10
fun main() {
val innerClass = InnerClass()
data += innerClass.addFun()
println("data = ${data}")
}
inner class InnerClass {
var data = 1
fun addFun(): Int {
return data + 1
}
}
}
val mainClass = MainClass()
mainClass.main() //打印 data = 12
3. Interface
@Test
fun test() {
println("execute success")
callback.success()
println("execute error")
callback.error()
/*
execute success
success
execute error
error
*/
}
val callback = object : Callback {
override fun success() {
println("success")
}
override fun error() {
println("error")
}
}
interface Callback {
fun success()
fun error()
}
4. enum class 用法
@Test
fun test() {
var tag = EnumClass.Data1.getTag()
println(tag)//打印 0 = Data1
tag = EnumClass.Data2.getTag()
println(tag)//打印 1 = Data2
tag = EnumClass.Data3.getTag()
println(tag)//打印 2 = Data3
var color = Colors.RED
println("color = $color, color.color ${color.color}")
//color = RED, color.color 0xFF0000
color = Colors.GREEN
println("color = $color, color.color ${color.color}")
//color = GREEN, color.color 0x00FF00
color = Colors.BLUE
println("color = $color, color.color ${color.color}")
//color = BLUE, color.color 0x0000FF
}
enum class EnumClass(private val index: Int) {
Data1(0),
Data2(1),
Data3(2);
fun getTag(): String {
return "$index = $name"
}
}
enum class Colors(val color: String) {
RED("0xFF0000"),
GREEN("0x00FF00"),
BLUE("0x0000FF")
}
enum class RGB { RED, GREEN, BLUE }
inline fun <reified T : Enum> printAllValues() {
print(enumValues().joinToString { it.name })
}
@Test
fun test() {
printAllValues() // 输出 RED, GREEN, BLUE
//enum都有 name: String與 ordinal: Int
}
5. sealed class 用法
//密封類不允許有非-private 構造函數(其構造函數默認為 private)
sealed class SealedClass {
data class onFinished(val data: Int): SealedClass()
object onError: SealedClass()
}
fun createSealedClass(index: Int): SealedClass {
when(index) {
0 -> {
return SealedClass.onFinished(30)
}
else -> {
return SealedClass.onError
}
}
}
@Test
fun test() {
val sealedClass = createSealedClass(0)
when(sealedClass) {
SealedClass.onError -> {
}
//data class 這裡需要使用 is
is SealedClass.onFinished -> {
//打印 30
println("data = ${sealedClass.data}")
}
}
}
6. open 用法
//open class 才可以給class繼承
//open fun 才可以繼承方法改寫
open class BaseClass {
open fun test1(value: Int) {
//打印 BaseClass test1 20
println("BaseClass test1 $value")
}
fun test2(value: Int) {
//打印 BaseClass test2 25
println("BaseClass test2 $value")
}
}
//使用override繼承
class TextClass: BaseClass() {
override fun test1(value: Int) {
super.test1(value)
//打印 TextClass test1 30
println("TextClass test1 ${value+10}")
}
}
@Test
fun test() {
val testClass = TextClass()
testClass.test1(20)
testClass.test2(25)
}
//最後打印
/*
BaseClass test1 20
TextClass test1 30
BaseClass test2 25
*/
//可讀可寫 mutableMapOf
//只可讀 mapOf
//第一種:可讀可寫
val map = mutableMapOf("a" to 20, "b" to "AAA", "c" to 10.0)
map["a"] = 32
println("map ${map["a"]}") //打印 map 32
//第二種:可讀不可寫
val map2 = mapOf("a" to 1, "b" to 2, "c" to 3)
map2["a"] = 32 //錯誤導致無法編譯
println("map ${map["a"]}") //打印 map 1
//遍歷map
for((key, value) in map2) {
println("key = $key, value = $value")
}
4. 內部靜態變數,使用lib的方法也如下
class AAA {
companion object {
const val data1 = 1
const val data2 = 2
init {
System.loadLibrary("native-lib")
}
}
}
class BBB {
fun setData(data: Int) {
AAA.data1 = 2
if(AAA.data2 == 2) {
println("data = ${AAA.data2}")
}
}
}
5. 靜態變數
object SDK {
var init: Boolean? = false
}
class main {
fun initData(flag: Boolean?) {
if(SDK.init != null) {
SDK.init = true
}
}
}
6. 如果資料是?型態宣告
fun isCount(data: Int?): Int {
return data ?: return -1//如果data is null回傳-1
}
@Test
fun test1() {
println(isCount(0))//0
println(isCount(null))//-1
}
7. !!使用方法
舉例強制將Int?轉Int,注意使用!!,如果是null將會發生異常
private var data: Int? = 1
fun checkData(): Int {
if(data != null) {
return data!!
}
return -1
}
//多個val data就可以去掉!!囉
private var data: Int? = 1
fun checkData(): Int {
val data = data
if(data != null) {
return data
}
return -1
}
8. data class用法
data class Book(var name: String, var price: Int) {
fun plusPrice(value: Int) {
//plus 是將price+value在返回值
price = price.plus(value)
}
fun minusPrice(value: Int) {
//minus 是將price-value在返回值
price = price.minus(value)
}
}
val book: Book? = Book("AABBCC", 59)
println("price = ${book?.price}") //輸出59
book?.plusPrice(10)
println("price = ${book?.price}") //輸出69
book?.minusPrice(20)
println("price = ${book?.price}") //輸出49
data class TestDataClass(
var name: String = "Apple",
val value: Int = 0
)
class TestClass(
val name: String = "Apple",
var value: Int = 0
)
@Test
fun test1() {
val testDataClass = TestDataClass().also {
it.name = "Windows"
}
val testClass = TestClass().also {
it.value = 1
}
//data class覆寫了toString
println(testDataClass.toString())
println(testClass.toString())
//打印如下
//TestDataClass(name=Windows, value=0)
//com.xxx.ExampleUnitTest$TestClass@2fd66ad3
//多了快速取值的方法
val (name, value) = testDataClass
println("name = $name, value = $value")
//打印如下
// name = Windows, value = 0
//克隆一份一模一樣資料的
val testDataClass1 = testDataClass.copy()
println(testDataClass)
println(testDataClass1)
//打印資料如下
//TestDataClass(name=Windows, value=0)
//TestDataClass(name=Windows, value=0)
}
//test2 與 test3比較就可以明顯看出class與data class的不同了
@Test
fun test2() {
val testDataClass = TestDataClass().also {
it.name = "Windows"
}
val testDataClass1 = TestDataClass().also {
it.name = "Windows"
}
//記憶體位置不同
println(testDataClass === testDataClass1)
//false
//相同物件
println(testDataClass == testDataClass1)
//true
println(testDataClass.hashCode())
//-1050734083
println(testDataClass1.hashCode())
//-1050734083 明顯看到相同
testDataClass1.name = "Apple"
//變更testDataClass1 名稱
println(testDataClass === testDataClass1)
//false
println(testDataClass == testDataClass1)
//false
println(testDataClass.hashCode())
//-1050734083
println(testDataClass1.hashCode())
//1967772678
println(testDataClass.toString())
//TestDataClass(name=Windows, value=0)
println(testDataClass1.toString())
//TestDataClass(name=Apple, value=0)
}
@Test
fun test3() {
val testClass = TestClass().also {
it.name = "Windows"
}
val testClass1 = TestClass().also {
it.name = "Windows"
}
//記憶體位置不同
println(testClass === testClass1)
//false
//相同物件
println(testClass == testClass1)
//false
println(testClass.hashCode())
//1375995437
println(testClass1.hashCode())
//1338841523 明顯看到不同
testClass1.name = "Apple"
//變更testClass1 名稱
println(testClass === testClass1)
//false
println(testClass == testClass1)
//false
println(testClass.hashCode())
//1375995437
println(testClass1.hashCode())
//1338841523
}
//此處有個重點在Kotlin與Java上的差異
1.Referential Equality 參考性 與記憶體有關
2.Structural Equality 結構性 Object相等
Java
Referential Equality ==
Structural Equality equals
Kotlin
Referential Equality ===
Structural Equality == , equals
9. 強制轉型
fun main() {
var data1: String = "1234"
var data2: Int = 20
onDataChange(data1)
onDataChange(data2)
}
fun onDataChange(data: Any) {
val data1: String? = data as? String
val data2: Int? = data as? Int
if(data1 != null) {
//有資料
println("{data1 ${data1}}")
}
if(data2 != null) {
//有資料
println("{data2 ${data2}}")
}
}
10. run、with、apply、also用法
run+let,run回傳最後一行給let,let再回傳最後一行,給result做為結果
data class Book(var name: String, var price: Int) {
fun plusPrice(value: Int) {
//plus 是將price+value在返回值
price = price.plus(value)
}
fun minusPrice(value: Int) {
//minus 是將price-value在返回值
price = price.minus(value)
}
}
//第一種
val book: Book? = Book("apple", 10)
val result = book?.run {
//run內部時,是this
println("this $this ------- name $name")
true
}.let {//run 和 with都是回傳最後一行
//此時的it是true
println("let $it")
it != null
}
println("result $result")
//打印
//this Book(name=apple, price=10) ------- name apple
//let true
//result true
//第二種
val book: Book? = null
val result = book?.run {
//run內部時,是this
println("this $this ------- name $name")
true
}.let {//run 和 with都是回傳最後一行
//此時的it是true
println("let $it")
it != null
}
println("result $result")
//此處run不會進入,因為run不會處理null,如果要確保let不是null改用with
//打印let null
//result false
當with是null時,最後回傳的依然是最後一行 因為with的內部可以有?
//第一種
val book: Book? = null
val result = with(book as Book?) {
println("this $this")
true
}.let {
//因為with可以在那處理null
//不會因為null直接到let
//所以基本上就是with的最後一行資料進來
it != null
}
println("result $result")
//打印
//this null
//result true
//第二種
val book: Book? = Book("Apple", 10)
val result = with(book as Book?) {
println("this $this")
true
}.let {
//因為with可以在那處理null
//不會因為null直接到let
//所以基本上就是with的最後一行資料進來
it != null
}
println("result $result")
//打印
//this Book(name=Apple, price=10)
//result true