Day1 可见性
参考
在 Kotlin 中一切都是默认 public 的。在Kotlin中,存在private、protected、internal和 public四种修饰符,它们可用于修饰类、对象、接口、构造器、函数、属性、以及属性的设值方法等。
// 默认public
val isVisible = true
// 只有在相同源文件内可见
private val isHidden = true
// 同一模块内可见
internal val almostVisible = true
class Foo{
// 默认public
val isVisible = true
// 只能被本类或其子类访问
protected val isInheritable = true
// 只能被本类访问
private val isHidden = true
// 同一模块可见
internal val isMan = true
}
修饰符 | 类成员 | 顶层声明 |
---|---|---|
public(默认) | 所有地方可见 | 所有地方可见 |
internal | 模块中可见 | 模块中可见 |
protected | 子类中可见 | - |
private | 类中可见 | 文件中可见 |
内部类和嵌套类:默认是嵌套类
类A在另一个类B中声明 | 在Java中 | 在Kotlin中 |
---|---|---|
嵌套类(不存储外部类的引用) | static class A | class A |
内部类(存贮外部类的引用) | class A | inner Class A |
/**
* 内部类
*/
class Outer{
inner class Inner{
fun getOuterReference(): Outer = this@Outer //获取外部类的引用
}
}
Day2 可空性
参考
Kotlin Reference: Null Safety
Kotlin教程(四)可空性
可空类型与非空类型
如果一个变量可能为空,然后对这个变量直接进行调用是不安全的,很容易造成NullPointerException,通常我们的办法是加个if判断,但是用java写个if在Kotliln看来语句也是听多的。所以Kotlin给变量增加了可空类型和非空类型特性。
通常声明一个变量,如果变量类型后面没有加?,那么它是非空类型,也就是它不能为null
例子:
没有加?是非空类型
非空类型变量a赋值为null,直接爆红
但是我加了?就正常了,证明加了?表示这个变量为可空类型,可以赋值为null
加?是可空类型非空判断与安全调用
加? 和不加可以看做是两种类型,不能直接调用可空类型的方法,只有与null进行比较后,编译器才会智能转换这个类型。
例如:
fun strLen(s: String?) = if(s != null) s.length else 0
这样s才能转为非空类型,才能调用其length属性
为了简化是否为null的if语句,Kotlin提供了?.这个安全调用运算符,它把一次null检查和一次方法调用合并成一个操作,例如例如表达式s?.toUpperCase() 等同于if (s != null) s.toUpperCase() else null,换句话说,如果你试图调用一个非空值的方法,这次方法调用会被正常地执行。但如果值是null,这次调用不会发生,而整个表达式的值为null。因此表达式s?.toUpperCase() 的返回类型是String?
安全调用支持链式调用,很实用,例:
bob?.department?.head?.name
Elvis 操作符
对于一个可为空的引用 r,我们可以说“如果 r 不为空,则使用它,否则使用另外的非空值 x”
原来写法:
val l: Int = if (b != null) b.length else -1
使用Elvis写法:
val l: Int = b?.length ?: -1
如果 ?: 左边的表达式不为空,则 Elvis 操作符返回该表达式;否则返回右边的表达式。注意只有在左边的表达式不为空时,才会计算右边的表达式。
因为在 Kotlin 中 throw 和 return 也属于表达式,所以它们也可以放在 Elvis 操作符的右边。这一点非常方便,如在检查函数参数时:
fun foo(node: Node): String? {
val parent = node.getParent() ?: return null
val name = node.getName() ?: throw IllegalArgumentException("name expected")
// ...
}
!!操作符
如果你想要看到NPE,就是让他像用Java一样可能抛NPE,那么你要显示的在变量后面加!!,不过这样的情况一般是在你100%确定该变量不会为空。
例如:
var a: String? = "abc"
var b: Int = a!!.length
安全转换
常规的转换会在对象不属于目标类型时导致 ClassCastException。此外还可以使用安全转换,安全转换会在转换失败时返回 null:
val aInt: Int? = a as? Int
Day3 String模板
格式化字符串?将$放在变量名的前面去表达字符串中的变量和表达式。使用 ${expression} 求表达式的值。
例子:
val i = 21
val s = "I'm $i years old! "
println(s)
val b = 2
val s2 = "after $b years,I'm ${i+b} years old!"
println(s2)
Day4 when表达式
参考文章
31 天,从浅到深轻松学习 Kotlin
kotlin中的when:强大的switch
when相当于switch,但是Kotlin的when是个强大的switch,几乎可以匹配任何东西,字面值,枚举,数组范围
像用Java的switch一样使用
when(条件){
条件值1 -> 执行语句1
条件值2 -> 执行语句2
条件值3 -> 执行语句3
else -> 执行语句4
}
自动转换类型
when (view) {
is TextView -> toast(view.text)
is RecyclerView -> toast("Item count = ${view.adapter.itemCount}")
is SearchView -> toast("Current query: ${view.query}")
else -> toast("View type not supported")
}
无自变量when,类似if else的含义,可做表达式
val res = when {
x in 1..10 -> "cheap"
s.contains("hello") -> "it's a welcome!"
v is ViewGroup -> "child count: ${v.getChildCount()}"
else -> ""
}
Day5 循环,范围表达式与解构
for(i in 1..100){
...
}
for(i in 100 downTo 1){
...
}
val array = arrayOf("a","b","c")
for (i in 1 until array.size step 2){
println(i)
}
for ((index,element) in array.withIndex()){
...
}
val map = mapOf(1 to "one", 2 to "two")
for ((key,value) in map){
...
}
Day6 属性
再Kotlin中,类可以具有可变和只读属性,默认情况下生成getter和setter,也可以自定义。
class User{
// 只读属性
val id: String = ""
// 可变属性,默认有getter和setter
var name: String = ""
var surname: String = ""
get() = surname.toUpperCase()//自定义getter
var email: String = ""
set(value){// 自定义setter
if (isEmailValid(value)) field = value
}
}
Day7 解构
Android KTX 使用解构来分配颜色的组件值。您可以在您的类中使用解构,或者扩展现有的类来添加解构。
val (red,green,blue) = color
val (left,top,right,bottom) = react
val (x,y) = point
解构声明可以一次声明多个变量,任何表达式都可以出现在解构声明的右侧,只要我们可以对它调用所需数量的component函数(注意:componentN()函数需要用operator关键字修饰,以允许其在解构声明声明中使用它们)。数据类自动声明component函数。
data class Person(val name: String = "Kotlin",val sex: String = "男",val age: Int = 20)
fun foo(){
var (name,sex,age) = Person()
var (_,sex2,age2) = Person()
println("name:$name,sex:$sex,age:$age")
}
也可以在for循环中使用解构声明,只要集合中的每个元素提供有componentN()方法
var collections= arrayListOf<Person>()//list集合中元素要为其每个成员属性提供有componentN方法
for((name,sex,age) in collections){
println("name=$name&sex=$sex&age=$age")
}
也可以对映射使用解构赋值。因为map结构提供函数 component1() 和 component2() 来将每个元素呈现为一对。
var map= mutableMapOf<String,String>()
map.put("name","Kotlin")
map.put("sex","男")
map.put("age","13")
for ((key,value) in map){
println("$key=$value")
}
Day 8 简单的bundle
通过简洁的方式创建bundle,不调用putString,putInt,或它们的20个方法中的任何一个。一个调用生成一个新的bundle,甚至可以处理Arrays,bundle很像map
val bundle = bundleOf(
"KEY_INT" to 1,
"KEY_LONG" to 2L,
"KEY_BOOLEAN" to true,
"KEY_NULL" to null,
"KEY_ARRAY" to arrayOf(1,2)
)
val testMap = mapOf(
"int" to 1,
"long" to 2L,
"boolean" to true,
"null" to null,
"array" to arrayOf(1,2)
)
Day 9 Parcelize
习惯Parcelable的速度,但不喜欢写所有的代码?和@Parcelize打个招呼.
@Parcelize
data class User(val name: String, val occupation: Work): Parcelable
// build.gradle
androidExtensions {
// enale experimental kotlin features in gradle to enable Parcelize
experimental = true
}
Day10 Data类和equality
可以创建一数据类,它可以默认实现生成equals()方法相当于hashCode(),toString()和copy(),并检查结构是否相等。
fun main(args: Array<String>){
val user1 = User("wang","123456","12345")
val user2 = User("wang","123456","12345")
println("user1 equals user2 :${user1 == user2}")
}
data class User(val name: String,
val email: String,
val address: String)
Day11 简化postDelay
Android KTX已经为Handler做了个小包装,类似于传闭包的形式传入参数。
// android KTX api
fun Handler.postDelayed(
delay:Int,
token: Any? = null,
action: () -> Unit
)
handle.postDelayed(50){
// pass a lambda to postDelayed
}
Day12 默认参数
如果方法中的参数太多,Kotlin可以在函数中指定默认参数值,使用命名参数使代码更具有可读性。
class BulletPointSpan(
private val bulletRadius: Float = DEFAULT_RADIUS,
private val gapWidth: Int = DEFAULT_GAP_WIDTH,
private val color: Int = Color.BLACK
){...}
// 只用默认值
val bulletPointSpan = BulletPointSpan()
// 传第一个参数值
val bulletPointSpan2 = BulletPointSpan(resources.getDimension(R.dimen.radius))
// 传最后一个参数的值
val bulletPointSpan3 = BulletPointSpan(color = Color.RED)
Day13 java调用Kotlin
java和,Kotlin混用,默认情况下,编译器可以生成顶级类,名称为YourFileKt,可以通过使用@file:JvmName注释文件来更改它。
ShapesGenerator.kt
package com.newtrekwang.kotlinprac
fun generateCicir():String{
return "circle"
}
fun generateRect():String{
return "rect"
}
val WIDTH : Int = 100
var height: Int = 300
java调用
public static void main(String argv[]){
String circle = ShapesGeneratorKt.generateCicir();
int width = ShapesGeneratorKt.getWIDTH();
int height = ShapesGeneratorKt.getHeight();
ShapesGeneratorKt.setHeight(200);
}
使用file注解
@file:JvmName("Test")
package com.newtrekwang.kotlinprac
fun generateCicir():String{
return "circle"
}
fun generateRect():String{
return "rect"
}
val WIDTH : Int = 100
var height: Int = 300
java调用
public static void main(String argv[]){
String circle = Test.generateCicir();
int width = Test.getWIDTH();
int height = Test.getHeight();
Test.setHeight(200);
}
Day14 在没有迭代器的情况下迭代类型
迭代器用在了有趣的地方!Android KTX 将迭代器添加到 viewGroup 和 sparseArray。要定义迭代器扩展请使用 operator 关键字。 Foreach 循环将使用扩展名!
可惜androidKTX要最新版开发版AS才能用。
// Andrfoid KTX
for(view in viewGroup){}
for(key in sparseArray){}
// 你的project
operator Waterfall.iterator(){
// 添加一个迭代器给waterFall类
}
for(item in myClass){}
第二周小结:
这周我们更深入的学习了 Kotlin 的特性:简洁 bundle,迭代,Data,postDelay,默认参数,序列化。
Day 15 Sealed Class
Kotlin的sealed类可以让你轻松的处理错误数据
密封类的好处在于:使用when表达式,如果能覆盖所有情况,就无需再添加else子句
sealed class NetworkResult
data class Success(val result: String):NetworkResult()
data class Failure(val error: Error):NetworkResult()
fun useSealedClass(networkResult: NetworkResult){
when(networkResult){
is Success -> showResult(networkResult.result)
is Failure -> showError(networkResult.error)
}
}
如果将sealed类用在RecyclerView的Adapter中,非常适合于ViewHolders,用一组干净的类型明确地分派给每个持有者。用作表达式时,如果有类型不匹配,编译器将会出错。
override fun onBindViewHolder(holder: SealedAdapterViewHolder?,position: Int){
when(holder){
is HeaderHolder -> {...}
is DetailHolder -> {....}
}
}
使用RecyclerView,如果我们有很多来自RecyclerView中的item的回调,比如一个点击,分享和删除item的项目,我们可以用sealed类,一个回调处理所有的事情!
sealed class DetailItemClickEvent
data class DetailBodyClick(val section: Int): DetailItemClickEvent()
data class ShareClick(val platform: String): DetailItemClickEvent()
data class DeleteClick(val confirmed: Boolean): DetailItemClickEvent()
interface ItemOnClickListener{
fun onClick(detailItemClickEvent: DetailItemClickEvent)
}
class MyHander: ItemOnClickListener{
override fun onClick(detailItemClickEvent: DetailItemClickEvent) {
when(detailItemClickEvent){
is DetailBodyClick -> {...}
is ShareClick -> {...}
is DeleteClick -> {...}
}
}
}
Day 16 懒加载
通过使用懒加载,可以省去昂贵的属性初始化的成本直到它们真正需要时。计算值然后保存并为了未来的任何时候的调用。
lazy 应用于单例模式(if-null-then-init-else-return),而且当且仅当变量被第一次调用的时候,委托方法才会执行。
val preference : String by lazy {
sharedPreferences.getString(PREFERENCE_KEY)
}
Day 17 lateinit
在kotlin中不为空的对象必须初始化,var 变量加这个关键字表示这个变量迟早会初始化,不过lateinit只能用于var
例如,Activity里的成员初始化
class MyActivity: AppCompatActivity(){
lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundler?){
recyclerView = findViewById(R.id.recycler_view)
}
}
Day18 要求(require)和检查(check)
判断方法参数的有效,可以用require在使用前检查它们,如果它们是无效的,将会抛出IllegalArgumentException
fun setName(name: String){
require(name.isNotEmpty()){ "Invalid name" }
}
封闭类的状态是否正确,可以使用check来验证,如果检查的值为false,它将抛出IllegalStateException
fun logout(){
check(isLogin()){"没有登录!"}
//todo
}
本质就是Kotlin标准库定义了个require和check含闭包的方法
网友评论