Kotlin学习笔记——归纳整理
Posted Yawn,
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin学习笔记——归纳整理相关的知识,希望对你有一定的参考价值。
目录
Kotlin简介来自于著名的IDE IntelliJ IDEA(android Studio基于此开发) 软件开发公司 JetBrains(位于东欧捷克)
是一种基于JVM的静态类型编程语言,2017年谷歌I/O大会确定Kotlin为Android的官方语言
一:基础语法
1.1 特性
在Kotlin中定义包与Java有点不同,在Kotlin中目录与包结构无需匹配,
Kotlin的源码可以在磁盘上的任意位置。
// 包格式 和 java 一致
package com.ymc.hellokotlin
fun main(args: Array<String>) {
println("Hello world")
println(max(2,3))
}
fun max(a:Int ,b:Int):Int{
return if(a>b) a else b;
}
- main 函数不需要在class 中就可以运行
- fun 代表一个函数,后边紧跟函数名称,参数列表和返回值类型
- 参数实现写 参数名称 然后冒号隔开,再写参数类型(和java 相反)
- 函数的返回值是在后边的(和java刚好相反的)当然 有返回值 也可以不写返回类型(前提是:只有表达式体 函数返回类型可以省略,如果是代码块体函数就必须要 写明函数返回类型),因为kotlin 通过 类型推导 也是可以知道返回值类型的
- system.out.println 被包装为 println
- 在行末可以省略 分号 (类似 js)
- 看到max函数中 if类似于三元表达式 kotlin中,if 是有结果值的表达式
- 如果返回值 类似于 java 中的 void 则可以写成 :Unit ,当然也可以省略不写
在kotlin 中,除了部分循环(for do 和 do/while)大多控制结构都是表达式,是有返回值的。另一方面 java 中的赋值语句为表达式,而kotlin 中则为语句。
与Java定义包名一样,在源文件的开头定义包名:但是不同的是,包名和文件夹路径可以不一致。
1.2 变量
kotlin 的变量声明是可以省略类型的,所以kotlin 变量声明有别于java ,kotlin变量声明顺序为关键字变量名称类型(可不加),如果变量没有初始化则需要明确表明变量类型。
常量与变量都可以没有初始化值,但是在引用前必须初始化编译器支持自动类型判断,即声明时可以不指定类型,由编译器判断。如果不在声明的时候初始化则必须提供变量的类型
主要分为两种定义:
(官方推荐 尽量使用val 声明变量,使程序更接近函数式编程风格)
- val :不可变引用 ,在val声明变量后不能再初始化之外再次赋值与java final一致
- var :可变引用 , 该类型变量可以随便赋值
1.2.1 可变变量的定义: var 关键字
var <变量名> : <变量类型> = <初始值>
var sum: Int = 3
sum = 8
//由于编译支持类型推导可以省略Int类型
var sum = 3
sum = 8
1.2.2不可变变量的定义: val 关键字
( 不能进行二次赋值,类似Java中的final类型)
val <常量名> : <常量类型> = <初始值>
val sum: Int //没有赋值初始化之前必须指定类型
sum = 5
1.2.3 常量
已知值的属性可以使用 const 修饰符标记为 编译期常量。需要满足如下几种条件 (类似 java 中的 constanUtil 中的 常量值)
- 位于顶层或者是 object 的一个成员
- 用 String 或原生类型 值初始化
- 没有自定义 getter
const val SUBSYSTEM_KEY: String = "key"
@Deprecated(SUBSYSTEM_KEY) fun foo() { …… }
1.2.4 字符串模板
字符串可以包含模板表达式,以$开始
val book = Book("Thinking In Java", 59.0f, "Unknown")
val extraValue = "extra"
Log.d("MainActivity", "book.name = ${book.name}; book.price=${book.price};extraValue=$extraValue")
1.3 类与属性
1.3.1 基础
在Kotlin中所有类都有一个共同的超类Any
//类定义,继承类和实现接口
class FeedBackActivity : NativeBaseActivity(), View.OnLongClickListener, BitmapUtil.SaveImageCall {
}
创建类的实例
val invoice = Invoice()
val customer = Customer("Joe Smith")
//注意 Kotlin 并没有 new 关键字。
我们比较一下 java 和 kotlin 中类的写法的不同
java:
public class DemoBean {
private final String name;
public DemoBean(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Kotlin:
class DemoBean(val name: String)
类是将数据和处理数据的代码封装成一个单一的实体。
class Person{
var name :String = "ymc" // 可读可写
val isMarried : Boolean = false // 只读
}
上段代码中 isMarried 会生成一个字段,一个getter ,而 name 则会生成 一个 getter和一个 setter 。
kotlin 在你声明属性的时候,你就声明了对应的访问器,默认的访问器 就是返回值的 getter 和 更新数值的 setter ,kotlin 会暴漏一个 getName 方法,当然我们也可以自定义访问器。
fun main(args: Array<String>) {
var person = Person()
println(person.name +";"+ person.isMarried )
}
可以看到我们直接调用而不需要get,感觉很像js…
1.3.2 自定义访问器
class Person{
var name :String = "ymc"
var sex : Int = 0; // 0 为女性 1为男性
val isMarried : Boolean = false
val isBoy :Boolean
get() {
return if(sex==1) true else false
}
}
将Person 添加 sex 和 isboy 属性,重写getter ,这样就可以做到自定义访问器。
如果 我们设置属性为val ,但是通过自定义 getter修改属性?
fun main(args: Array<String>) {
val name = "Hello Kotlin"
name = "Hello Java"
}
Error:(8, 5) Kotlin: Val cannot be reassigned
如果单纯的修改,则会报错
接下来我们通过 自定义访问器 看看
class RandomNum {
val num: Int
get() = Random().nextInt()
}
fun main(args: Array<String>) {
println("the num is ${RandomNum().num}")
}
the num is -1251923160
the num is -1527833641
总结: 由以上的例子可以说明假设一是成立的,在Kotlin中的val修饰的变量不能说是不可变的,而只能说仅仅具有可读权限。
1.3.3 备用字段
kotlin 中并不允许使用字段,这个时候我们就可以使用备用字段,比如下段代码,起到局部变量的作用。
//初始化值会直接写入备用字段
var counter = 0
get() = field // field可以理解为自己本身
set(value) {
if (value >= 0)
field = value
}
// 这种情况并不需要备用字段,所有不会生成备用字段
val isEmpty: Boolean
get() = this.size == 0
注意:field标识符只允许在属性的访问器函数内使用.
1.3.4 延迟初始化属性和变量
属性声明为非空类型必须在构造函数中初始化,为处理这种情况,你可以用 lateinit 修饰符标记该属性:
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // 直接解引用
}
}
这样的话,我们就可以不用再构造函数的时候对其进行初始化,后续在哪里需要调用 setup就可以。
1.4 函数
1.4.1 函数基本方法
Kotlin 中的函数使用 fun 关键字声明,参数即 name: type。参数用逗号隔开
fun double(x: Int): Int {
return 2 * x
}
//Int 返回类型可以省略
fun sum(a: Int, b: Int): Int {
return a + b
}
//也可以
fun sum(a: Int, b: Int) = a + b
减少方法重载
//支持默认参数值,减少方法重载
fun showToast(message: String, duration:Int = Toast.LENGTH_LONG) {
Toast.makeText(this, message, duration).show()
}
//调用方式:没有默认值的参数,调用时,必须填写
showToast("toast");
showToast("toast", Toast.LENGTH_LONG);
Main方法比较
//Java
public static void main(String[] args){
}
//Kotlin
fun main(args:Array<String>){
}
1.4.2 主次构造函数
在Kotlin中的一个类可以有一个主构造函数和一个或多个次构造函数
- 主构造函数:带有类名的为主构造函数(只有一个)
- 主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块
- 主构造函数中声明的属性可以是可变的(var)或只读的(val)
//如果构造函数有注解或可见性修饰符,这个 constructor 关键字是必需的,并且这些修饰符在constructor前面:
class Customer public @Inject constructor(name: String) { …… }
//无修饰可不写constructor关键字
class Customer (name: String) {
var a :Int = 1
init{……}
}
- 次构造函数:不带类名并且有constructor关键字修饰的函数为次构造函数(可以一个或多个),并且只能存在主构造函数代码块之内
//如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数用 this 关键字即可:
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
- 无参构造函数:
- 如果没有构造函数,将会默认一个无参数构造函数
- 如果主构造函数的所有的参数都有默认值,编译器会生成一个额外的无参构造函数,它将使用默认值。
//如构造函数为私有,需用private修饰
class DontCreateMe public constructor () {
}
class Customer(val customerName: String = ""){}
1.4.3 函数省略返回类型
下例是一个简单实例:
fun max(a:Int, b: Int): Int{
return if (a > b) a else b
}
当函数体是由单个表达式构成时,可以用这个表达式作为完整的函数体,并且去掉花括号和return语句,上面的例子就是这种情况,因此可以改写为:
fun max(a:Int, b: Int): Int = if (a > b) a else b
}
- 如果函数体写在花括号中,我们说这个函数有代码块体
- 如果它直接返回了一个表达式,它就有表达式体
对于 表达式体函数,可以省略返回类型,因为编译器会分析作为函数体的表达式,并把它的类型作为函数的返回类型,这种分析称为 类型推导。
上面的例子可以简化为:
fun max(a:Int, b: Int) = if (a > b) a else b
}
但是对于有返回值的代码块体函数,必须显示地写出返回类型和return语句。
如:
1.4.4 函数调用
val result = double(2)
Sample().foo() // 创建类 Sample 实例并调用 foo
1.5 控制流:if、when、for、while
1.5.1 区间
区间表达式由具有操作符形式 … 的 rangeTo 函数辅以 in 和 !in 形成。
if (i in 1..10) { // 等同于 1 <= i && i <= 10
println(i)
}
//顺序
for (i in 1..4) print(i) // 输出“1234”
for (i in 4..1) print(i) // 什么都不输出
//倒序
for (i in 4 downTo 1) print(i) // 输出“4321”
//要创建一个不包括其结束元素的区间,可以使用 until 函数:
for (i in 1 until 10) { // i in [1, 10) 排除了 10
println(i)
}
//能否以不等于 1 的任意步长迭代数字? 当然没问题, step() 函数有助于此:每循环到第二个元素就剔除
for (i in 1..4 step 2) print(i) // 输出“13”
for (i in 4 downTo 1 step 2) print(i) // 输出“42”
1.5.2 if表达式
if和Java用法一致
val max = if (a > b) a else b
1.5.3 when表达式
when它能完全取代switch/case,也可以用来取代 if-else if链,并且还有很多新的特性。
如果其他分支都不满足条件将会求值else分支。 如果 when 作为一个表达式使用,则必须有 else 分支, 除非编译器能够检测出所有的可能情况都已经覆盖了。
//分支条件可以是:Int,区间,方法,字符串,对象等
when (x) {
0, 1 -> print("x == 0 or x == 1")
in 2 -> print("x == 2")
in 3..8 -> print("x == 3..8")
parseInt(s)-> print("parseInt")
is String -> print("x is String")
else -> print("otherwise")
}
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")//智能转换
else -> false
}
//when 也可以用来取代 if-else if链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
1.5.4 for 循环
for (item in collection) print(item)
//循环体可以是一个代码块。
for (item: Int in ints) {
// ……
}
for (i in 1..3) {
println(i)//输出“123”
}
for (i in 6 downTo 0 step 2) {
println(i)//输出“6420”
}
//如果想索引遍历,可以通过indices函数
for (i in array.indices) {
println(array[i])
}
//或者你可以用库函数 withIndex:
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
//输出:
the element at 0 is a
the element at 1 is b
the element at 2 is c
1.5.5 While循环
while 和 do…while 照常使用
fun main(args: Array<String>) {
println("----while 使用-----")
var x = 5
while (x > 0) {
println( x--)
}
println("----do...while 使用-----")
var y = 5
do {
println(y--)
} while(y>0)
}
1.5.6 跳出循环
- return,默认从最直接包围它的函数或者匿名函数返回。
- break,终止最直接包围它的循环。
- continue,继续下一次最直接包围它的循环。
1.6 关键字,修饰符,特殊符号
关键字:
- Any:在Kotlin中所有类都有一个共同的超类Any,Any并不是Object
- open:修饰类:表示能被继承;修饰方法:表示需要重写
修饰符:
final、open、abstract、override对比
可见性修饰符:
类、对象、接口、构造函数、方法、属性和它们的 setter 都可以有 可见性修饰符。
在 Kotlin 中有这四个可见性修饰符:private、 protected、 internal 和 public。
如果没有显式指定修饰符的话,默认可见性是 public
internal —— 能见到类声明的 本模块内 的任何客户端都可见其 internal 成员
其它和Java一样
特殊符号:
-
?:表示当前是否对象可以为空
-
!!: 表示当前对象不为空的情况下执行
private var cloth_Rv: RecyclerView ?= null
cloth_Rv!!.setHasFixedSize(true)
1.7 object关键字
object关键字主要有三种使用场景
- 对象声明(object declaration)
- 伴生对象(companion object)
- 对象表达式(object expression)
1.7.1 对象声明(object declaration)
将类的声明和定义该类的单例对象结合在一起(即通过object就实现了单例模式)
对象声明中不能包含构造器(包括主构造器和次级构造器)
对象声明实例解析以及在kotlin和java代码中的调用
object UserManager {
fun saveUser()
}
// 反编译出的Java代码
public final class UserManager {
public static final UserManager INSTANCE;
public final void saveUser() {
}
private UserManager() {
}
static {
UserManager var0 = new UserManager();
INSTANCE = var0;
}
}
在kotlin和java代码中,它们的调用方式有点差别:
- kotlin代码调用:UserManager.saveUser()
- java代码调用:UserManager.INSTANCE.saveUser();
1.7.2 伴生对象(companion object)
Kotlin给Java开发者带来最大改变之一就是废弃了static修饰符。与Java不同的是在Kotlin的类中不允许你声明静态成员或方法。相反,你必须向类中添加Companion对象来包装这些静态引用: 差异看起来似乎很小,但是它有一些明显的不同。
在kotlin中是没有static关键字的,也就意味着没有静态方法和静态成员。那么在kotlin中如果想要表达这种概念,可以使用包级别函数(package-level funcation)和伴生对象(companion object)。
伴生对象语法形式:
class ClassName {
// 伴生对象名可以省略,默认为Companion
companion object 伴生对象名 {
// define field and method
}
}
伴生对象实例解析以及在kotlin和java代码中的调用:
class App {
companion object {
fun getAppContext() {}
}
}
// 反编译出的Java代码
public final class App {
public static final App.Companion Companion = new App.Companion((DefaultConstructorMarker)null);
public static final class Companion {
public final void getAppContext() {
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
与对象声明类似,它们在kotlin和java代码中的调用方式也有点差别:
- kotlin代码调用:App.getAppContext()
- java代码调用:App.Companion.getAppContext();
companion:这个关键字实际上只是一个快捷方式,允许你通过类名访问该对象的内容(如果伴生对象存在一个特定的类中,并且只是用到其中的方法或属性名称,那么伴生对象的类名可以省略不写)。就编译而言,下面的testCompanion()方法中的三行都是有效的语句。
class TopLevelClass {
companion object {
fun doSomeStuff() {
...
}
}
object FakeCompanion {
fun doOtherStuff() {
...
}
}
}
fun testCompanion() {
TopLevelClass.doSomeStuff()
TopLevelClass.Companion.doSomeStuff()
TopLevelClass.FakeCompanion.doOtherStuff()
}
为了兼容的公平性,companion关键字还提供了更多选项,尤其是与Java互操作性相关选项。果您尝试在Java类中编写相同的测试代码,调用方式可能会略有不同:
public void testCompanion() {
TopLevelClass.Companion.doSomeStuff(Kotlin学习之旅解决错误:kotlin.NotImplementedError: An operation is not implemented: Not yet implemented(代码片段
我似乎无法将我的Kotlin Recycler多视图代码整理出来
Kotlin 延迟初始化和密封类[第一行代码 Kotlin 学习笔记]