kotlin 学习的小知识点
1.单例模式
在Java中
public class Singleton{
//私有化,杜绝被本类外的其他类使用;静态,在代码加载时就创建
private static Singleton singleton = new Singleton();
//构造器私有,其他类无法创建该类的对象
private Singleton(){}
//只能通过get方法获取该类的对象
public static Singleton getSingleton() {
return singleton;
}
}
在Kotlin中
object Singleton{
//方法体1
//方法体2
}
调用方式
Singleton.方法名()
2.定义常量的关键字const
只有在单例类、companion object或顶层方法中才可以使用const关键字
3.companion object(伴生对象)
类似java中的static
companion这个关键字实际上只是一个快捷方式,允许你通过类名访问该对象的内容(如果伴生对象存在一个特定的类中,并且只是用到其中的方法或属性名称,那么伴生对象的类名可以省略不写)。就编译而言,下面的testCompanion()方法中的三行都是有效的语句。
class TopLevelClass {
//companion object修饰的类,类名可以不写,直接访问其中的方法
companion object {
fun doSomeStuff() {
...
}
}
//object修饰的类是单例类,需要通过类名访问其中的方法
object FakeCompanion {
fun doOtherStuff() {
...
}
}
}
fun testCompanion() {
TopLevelClass.doSomeStuff()
TopLevelClass.Companion.doSomeStuff()
TopLevelClass.FakeCompanion.doOtherStuff()
}
4.startActivity和startActivityForResult中intent的区别
第一种情况:
代码中的intent调用的是父类的getIntent()方法,这个点击事件可以执行,启动的Activity也能收到消息
thirdly_3_3_4_bt.setOnClickListener {
intent.putExtra("data","Hello,这个上一个Activity传来的消息")
intent.setClass(this,FourthActivity::class.java)
startActivity(intent)
}
第二种情况:
intent调用的是父类的getIntent()方法,需要下一个Activity返回消息
可以启动下一个Activity,启动的Activity可以收到消息,但是无法返回消息
//FirstActivity
thirdly_3_3_4_bt.setOnClickListener {
//不创建intent的对象,下一个Activity可以接收消息,无法返回消息
// val intent = Intent()
intent.putExtra("data","Hello,这个上一个Activity传来的消息")
intent.setClass(this,FourthActivity::class.java)
startActivityForResult(intent,1)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode){
1 -> if (resultCode == RESULT_OK){
val stringExtra = data?.getStringExtra("data")
Toast.makeText(this, "收到请求码为$requestCode 回复:$stringExtra", Toast.LENGTH_LONG).show()
}
}
//FourthActivity
private fun initView() {
fourth_activity_back_bt.setOnClickListener {
intent.putExtra("data","fourthActivity返回的消息")
setResult(RESULT_OK,intent)
finish()
}
}
5.list赋值操作
- var list
var list = listOf("apple","orange") //只读,不能对list中的元素增删改
list = listOf("111","222","333") //但是可以对list对象重新赋值
for (bean in list) println("list对象重新赋值"+bean)
list对象重新赋值111
list对象重新赋值222
list对象重新赋值333
var list = mutableListOf("apple","orange")//使用 mutableListOf定义list,可实现增删改查
2.val list
val list = listOf("apple","orange") //元素和list对象都是只读,都无法修改
list = listOf("111","222","333") //报错 ”Val cannot be reassigned“
- ArrayList定义的list与java中没有什么区别
var list1 = ArrayList<String >()//从源码可知,ArrayList定义方式为Java的定义方式
list1.add("tom")
list1.add("and")
list1.add("jerry")
list1[0] = "kity"
for (bean in list1) println(bean)
val list1 = ArrayList<String >() //var和val定义的list两者都可以增删改查
list1.add("tom")
list1.add("and")
list1.add("jerry")
list1[0] = "kity"
for (bean in list1) println(bean)
kity
and
jerry
- java定义的list可以赋值给kotlin定义的list,反之则报错
var list = mutableListOf("")
var list1 = ArrayList<String >()
list = list1
var list = mutableListOf("")
var list1 = ArrayList<String >()
list1 = list //报错:Type mismatch: inferred type is MutableList<String> but kotlin.collections.ArrayList<String> /* = java.util.ArrayList<String> */ was expected
6.var,val在类中限定,以及两个数据类的对象如何认定相等的
例 1:
两个属性,data1主构造器赋值,data2定义时赋值
class datas(var data1:String ){
var data2:String = "非构造参数"
}
fun main() {
var ex1 = datas("构造参数")
// ex1.data2 = "构造参数"
println(ex1.data2)
val ex2 = datas("构造参数")
println(ex2.data2)
println(ex1 == ex2)
}
打印结果:
默认对象的toString()打印的主构造器中的参数
非构造参数
非构造参数
false
例 2:
val修饰的变量和类的对象在赋值后不能再重新赋值了
data class datas(val data1:String ){
val data2:String = "非构造参数"
}
fun main() {
var ex1 = datas("构造参数")
ex1.data1 = "构造参数1"
ex1.data2 = "非构造参数2"
println(ex1.data2)
val ex2 = datas("构造参数")
ex2 = ex1
println(ex2.data2)
println(ex1 == ex2)
}
结果:
报错 7,8,11行报错。
Val cannot be reassigned
如果在实际应用中,如果对象的属性需要重新赋值,用var修饰
实例化类的时候kotlin建议val修饰,如用var修饰不会报错,系统会提示:
variable is never modified andcan be declared immutable use “val”(变量永远不会被修改,可以使用"val"来声明它是不可变的)
例 3:
两个对象的主构造器中属性的值相等,属性data2值不相同
但是结果作为数据类这两个对象是相等的,这个是不是属于Kotlin的BUG?
data class datas(var data1:String ){
var data2:String = "非构造参数"
}
fun main() {
var ex1 = datas("构造参数")
ex1.data2 = "非构造参数2"
println("ex1.data2 = "+ex1.data2)
val ex2 = datas("构造参数")
println("ex2.data2 = "+ex2.data2)
println("ex1是否等于ex2 ? " + (ex1 == ex2))
}
结果:
ex1.data2 = 非构造参数2
ex2.data2 = 非构造参数
ex1是否等于ex2 ? true
例 4:
如果类不用data修饰,定义为非数据类,则两个类就算属性相等,equals也是不相等的
class datas(var data1:String ){
var data2:String = "非构造参数"
}
fun main() {
var ex1 = datas("构造参数")
val ex2 = datas("构造参数")
println("ex1是否等于ex2 ? " + (ex1 == ex2))
}
结果:
ex1.data2 = 非构造参数
ex2.data2 = 非构造参数
ex1是否等于ex2 ? false
7.全局变量的创建
不建议使用
//创建msg值为null的全局变量
var msg : String ? = null
建议使用
//创建msg为全局变量,并稍后初始化
lateinit var msg :String
//判断msg是否初始化
if (!::msg.isInitialized){
//TODO
}
8.高阶函数
对高阶函数的理解:把一个函数作为参数传递到另一个函数中
在执行A函数时,插入一段B代码,这段B代码是调用A函数时创建的。
fun main(){
val num1 = 10
val num2 = 20
//插入的代码在调用的时候完成
val result1 = num1Andnum2(num1,num2){n1,n2->n1+n2}
val result2 = num1Andnum2(num1,num2){n1,n2->n1-n2}
println(result1)
println(result2)
}
//函数作为参数时,只需要定义好参数类型和返回值类型,返回值为空则为Unit
fun num1Andnum2(num1:Int,num2:Int,operation:(n1:Int,n2:Int)->Int): Int {
//不使用高阶函数时,这段代码的功能就时返回两个数的和
val sum = num1 + num2
//使用高阶函数后,还可以让这个和做其他操作,但是这个其他操作可能在不同的地方有不同的操作,所以由operation参数来做个接口。在本例中,是做+10或-10操作
val sum = operation(sum,10)
return sum
}
9.内置扩展函数 lei/also run/apply with
use函数
- 实现了Closeable接口的对象可调用use函数
- use函数会自动关闭调用者(无论中间是否出现异常)
- Kotlin的File对象和IO流操作变得行云流水
链接
let/also函数
- let可以配合可空性 “?”来使用,如果data=null 则不执行let内部代码,如果有返回值则直接返回null
- 在let中,用it表示引用对象,并可调用其方法,it不可省略。
- 返回值是语句块的最后一行的返回类型,若最后一行语句无返回值,则整个let语句块也无返回值
let和also的区别:also返回对象本身,在本例中返回list对象
//在此例中,判断list是否为空,为空直接返回null,否则返回最后一行,如最后一行无返回值,则整个let语句无返回值,
var result = list?.let {
it.add(1)
it.add(2)
it.add(3)
"ok"
}
//list后面没有?,则不判断list是否为空
var result = list.let {
"ok"
}
apply / with / run
作用基本相同,都是可以代替对象本身,直接调用对象的方法或者属性,区别则是使用时调用的方式不同,和返回的不同
返回对象本身
also:it
apply:this
返回最后一行的值
let:it
run:this
10. 类型强制转换 as
as
不安全的类型强制转换,如果转换不成功则会抛出异常
val y = 123
val x: String = y as String
as?
安全的类型强制转换,转换不成功则赋值null,但是要注意x的类型也要为可空类型
val y = 66
val x: String? = y as? String
println("x = $x") // x = null
在这里插入代码片
11.数组的遍历.withIndex
val intArray = intArrayOf(9,8,7,6,5,4)
for ((index,result) in intArray.withIndex()){
println("第$index 个元素,值是:$result")
}
12.定义函数类型的别名typealias
- 作用:如果函数类型在文件中编写次数较多,可以通过
typealias
给这个函数类型定名字
val sum = fun (a:Int,b:Int) = (a+b).toString()
fun test(a:Int,b:Int,printlnIntSum:(Int,Int)->String){
println(printlnIntSum(a, b))
}
test(1,2,sum)
示例中给函数类型(Int,Int)->String
定义了名字sumToSting
,以后在使用中就使用名字
//typealias 定义时在类的外部,和import在一起
typealias sumToSting = (Int,Int)->String
fun main() {
val sum = fun (a:Int,b:Int) = (a+b).toString()
fun test(a:Int,b:Int,printlnIntSum:sumToSting){
println(printlnIntSum(a, b))
}
test(1,2,sum)
}
13.isEmpty()与null与“”的区别
isEmpty()
分配了内存空间,值为空,是绝对的空,是一种有值(值 = 空)
“”
分配了内存空间,值为空字符串,是相对的空,是一种有值(值 = 空字串)
null
是未分配内存空间,无值,是一种无值(值不存在)
isEmpty()
并不是String类型所特有的方法,ArrayList,HashMap,HashSet也同样拥有此方法,原因在于isEmpty()实际上是Collection中定义的方法,而所有继承了该接口的方法都将会实现。
--------------------------------------------------分割线----------------------------------------------------------------
1.类的修饰符
1.sealed class 密封类
为了限制出现以下代码的情况:
when的分支条件中必须增加无用的else分支(书中叙述)
//实际使用时,就算没有unknown分支也不会报错
interface Result
class Success (val msg : String = "success") : Result
class Failure (val msg : String = "failure") : Result
fun test(result: Result) {
when(result){
is Success -> println(result.msg)
is Failure -> println(result.msg)
else -> println("未知")
}
}
为避免出现上述错误,使用sealed class代替interface
//书上说,密封类强制要求将每一个子类所对应的条件全部处理 但是一下这段代码没有报错,只是提示when下缺少unknown或者else的分支
sealed class Result
class Success (val msg : String = "success") : Result()
class Failure (val msg : String = "failure") : Result()
class Unknown (val msg : String = "unknown") : Result()
fun test(result: Result) {
when(result){
is Success -> println(result.msg)
is Failure -> println(result.msg)
//增加unknown或者else分支其中一个,就不会有提示
//is Unknown -> println(result.msg)
//else -> println("未知")
}
}