1、介绍
目前Android开发中,分为两派,Java派和Kotlin派,Java是宇宙开发第一语言,地位一直处于领先位置,但是Java会出现一个空指针的情况,所以kotlin的出现,填补了java的一些缺陷,但是也导致一些人习惯了Java语言,在转kotlin的时候,会经常写成java字段。所以我接下来把kotlin在开发过程常见的关键字和一些用法进行介绍,以及一些错误如何处理,看完基本就能正常上手写kotlin项目。
二、常见使用介绍
似曾相识,就是不会。看完恍然大悟。接下来对高频用法和高频关键字进行介绍
1、var 和val,变量声明
1.1 var 是声明为常量,有读与写权限
var a:Int=10,
a=20
1.2val 是声明为常量,相当于final,只能读,没有写权限
val b:Int=10
b=20//这行报错
2、对象的创建
var chidl:Child
如果直接使用报错
var child=Child()直接new一个新的
3、!!和?区别
!!:如果有空指针会被抛出
?:如果当前对象是空指针,将不会执行后面的操作
如下:
4、"?"的使用
1.var child:Child?=null
这种表示child为null,
如果你直接写成
var child:Child=null
直接报错
变成的初始化可以这样
var child:Child= Child() 声明成Child的类型,然后初始化 var child1=Child()
直接初始化成child类型
5、可变数组与不可变数组
在kotlin中,数组分为可变和不可变,可变是可以添加值,不可变就是声明后,就不能再add进去。
可变素组正常都是mutable开头,常见的有如下:
mutableListOf<>() mutableMapOf<>() mutableSetOf<>()
在这边,var和val对对象的修饰不是那么明显,即使你申请为val,可变数组还是可以操作的
6、静态类,关键字:object
我们在Java中会使用静态变量通过static来修饰,kotlin在修饰静态变量直接用object修饰类
object StaticMian { fun log() { MyLog.log("object is static") } }
使用:StaticMian.log()
如果设置类为静态,里面的方法都不需要再进行修饰,和其他类一样使用
静态类的内部类也可以
object DemoOut { object DemoIn{ fun log(){ MyLog.log("DemoIn") } } fun log(){ MyLog.log("DemoOut") } }
7、单利模式:
1.Synchronized
单利在Java作为工具类用的好多地方,kotlin的单利模式是通过在get方法中
object StaticMian { private var mian: StaticMian? = null @get:Synchronized val instance: StaticMian? get() { synchronized(StaticMian::class.java) { if (mian == null) { mian = StaticMian } } return mian } fun log() { MyLog.log("object is static") } }
关于在对象下方的get(),可以参考我的另一篇:Kotlin语法详解与实践教程,区分JAVA以及如何闭坑
修饰该对象是同步,只需要修饰该方法的get,指向Synchronized,和申明变量差不多
@+get:+关键字
var +变量名:+类型
第二种单利写法:
class TestSing { companion object { private var test: TestSing? = null; fun getInstance(): TestSing { synchronized(TestSing::class.java) { if (test == null) test = TestSing() } return test!! } } }
8、常量const关键字用法
const是修饰val的,val是只有读权限,如果用const来修饰,那就变成了一个常量,const 只允许在top-level级别和object,也就是是说当前类必须是通过object修饰的,否则不能使用
而且const修饰的变量必须要被初始化。
1.正确写法
const val a:Int = 0;
2.错误写法
const val a:Int;
3.const的出现:只能出现在静态类或者静态模块中,否则报错
9、接口使用使用
1.接口创建
interface MyInterface {
public fun change(msg: String): String;
public fun add(a: Int, b: Int): Int {
return a + b
}
}
也是通过关键字interface 来修饰
写法没有啥区别,唯独有争议的地方就是方法可以有方法体,有方法体只能当方法使用
2.继承接口
类和的继承和接口都是通过":"实现的
如果接口中定义的方法有方法体,在接口继承将不会需要重写,已被定义为方法了,可以直接调用,也可以重写
class MyTestDemo :MyInterface {
override fun change(msg: String): String {
return msg+"===change";
}
override fun add(a: Int, b: Int): Int {
return super.add(a, b)
}
}
fun main() {
var demo=MyTestDemo()
MyLog.log(demo.change("123"))
MyLog.log(demo.add(2,3))
}
3.参数为接口,如果View的onclick实践
1.直接和java写法一致:
demo.setInterface(object : MyInterface {
override fun change(msg: String) {
}
})
2.lambda表达式:
无参:直接{}在空号内写回调
demo.setOnclick { }
有参:直接将参数名按顺序列出来,指向方法体->
demo.addOnDestinationChangedListener {controller,destination,arguments-> toast("${destination.displayName}") }
10、多构造器以及继承类的构造器传参
非继承类构造器
class MyConstrDemo() {
constructor(name: String) : this() {
}
constructor(name: String,age:Int) : this() {
}
}
fun main() {
var demo1=MyConstrDemo()
var demo2=MyConstrDemo("")
var demo3=MyConstrDemo("",12)
}
默认构造器通过类名后面追加。如果多重载,通过关键字来完成constructor(),返回值就是默认构造器类型
类名后带参数的构造器又叫默认构造器,如果在类的内部通过constructor定义构造器,返回值类型一定是this(),this就是类名后面接的默认构造器。
继承类构造器:
open class Parent(name: String = "parent") {
}
class MyConstrDemo(name:String):Parent(name) {
constructor() : this("") {
}
constructor(name: String,age:Int) : this() {
}
}
和单类一样,只是要传参给父类。其他写法和非继承一样
11、lateinit 关键字
lateinit 关键是需要对变量进行初始化,切变量属性是private类型,切用来修饰var
在Android开发过程中,比较常见的就是lateinit var来定义view
lateinit var text=findViewById(id)as TextView
这种修饰是申请变量可以为null,一般简单类型不适合
1.错误,这种直接报错
lateinit var age:Int
同样,这样修饰过的变量不能去修改set()和get()方法
可以为null和不可以的示例:
lateinit var mp1:HashMap<String,String> var mp2:HashMap<String,String>
12、强制转换 as
as在kotlin中作为类型的强制转换,Java中 int a=(int) 100.123f
kotlin:
对象类型
a=b as Int
常见的view初始化如下:
var text=findViewById(id) asTextView
13、构造器对象使用
我们如果在kotlin中构造器中有对象变量,需要引用构造器变量,无须将构造器变量赋值到类的内部变量,可以直接通过var修饰完直接引用
class DataMapConst(var map:HashMap<String,String>?)
class DataMapConst(var map:HashMap<String,String>?) {
lateinit var mp1:HashMap<String,String>
constructor( msg:String):this(null){
}
fun log(){
MyLog.log(map!!?.toString())
}
inner class Child{
fun log(){
MyLog.log(map!!?.toString())
}
}
}
fun main() {
var map:HashMap<String,String>?=null
map= HashMap()
DataMapConst("ssss")
var demo=DataMapConst(map);
map.put("one","1")
map.put("two","1")
map.put("three","1")
demo.Child().log()
}
14、对象默认set()和get()使用
kotlin的变量都提供了set()和get() 方法,但是lateinit修饰过的变量,无法修改set()和get()方法
class People {
var name: String=""
set(value) {
field=value+"=123"
}
get() {
return field + "==modify"
}
var age: Int = 0
}
fun main() {
var people=People()
people.name="zhangshan"
MyLog.log(people.name)
}
set(value):这个value是 val,只能读,不可修改
get():需要一个return
field:是变量自身的值,如果了解Java反射的,应该明白,field是class获取到的变量
想了解Java反射,可以参考我的另一篇文字 Android Java反射与Proxy动态代理详解与使用基础篇(一)
这样我们就能随意修改field值,以及返回值。这类似Java hook技术。同时,也破坏了数据原有的安全性,谨慎使用。
15、companion关键字
companion只能修饰object,且该object必须是内部类静态模块。
常见的用法如下
class TestSing {
companion object {
//类的静态模块
}
}
所以object有两种用法
1.修饰类为静态内
2.和companion搭档,companion object修饰非静态类的静态模块
如果类为静态类,就不能这样使用,因为静态类,里面的方法和熟悉都可以直接使用,再修饰成静态模块就会报错。
所以 :只能出现在非静态类中
companion object{
}
这种用法最为非静态类中的静态模块
16、open关键字
kotlin中的类和方法都是无法直接被继承或者重写,类似Java中被修饰了,但是可以通过open来修饰就变成了开放型类和方法。
open class Base {
open fun log(){
}
}
class TestBase :Base(){
override fun log() {
super.log()
}
}
17、Any,Unit,Nothing,Void
1.Any:相当于java中的object使用
Unit:是一个object类,类似java中的void
fun aa(): Unit { }
Nothing:是一个有私有构造器的类,常作为定义异常抛出类使用
fun bb():Nothing=throw java.lang.Exception("error");
也经常在继承提示作为TD使用
@kotlin.internal.InlineOnly
public inline fun TODO(reason: String): Nothing = throw NotImplementedError("An operation is not implemented: $reason")
Void:作为空来使用,常作为返回一个空类型。
fun cc(): Void? { var map:Any; return null; }
上面四种类型,使用最多的是Any和Nothing,关于unit和void可以作为了解,不做参考
18、inner:内部类修饰
如果类中嵌套类,我们常叫成为内部类,在Java中直接写,但是kotlin中,如果需要使用内部类,需要用一个inner来修饰
class People {
inner class MySon {
var name: String = ""
fun log() {
MyLog.log(name)
}
}
}
fun main() {
var sun = People().MySon()
sun.name="123"
sun.log()
}
注意: 在使用过程中,内部类的使用如下
正确:
var sun = People().MySon()
错误:
var sun = People.MySon()
19、when
when的使用和Java中的Switch一个逻辑,但是格式右边
val a = 10
when (a) {
0, 10 -> {
MyLog.log("0-10")
}
20 -> {
MyLog.log("==20")
}
30 -> {
MyLog.log("==30")
}
else -> {
MyLog.log("not value")
}
}
when( 表达式)
0,10:a>=0&&a<=10
相当于
case 0:
case10:
break
else相当于default
注意:任何模块结束自带break。
20.、is的使用
在kotlin中,类型的判断是 is,和java instanceof一样
var b=10
if (b is Int)
{
MyLog.log("Int")
}
21、in在for中的使用
in在for循环中使用比较多,和迭代去关联。但是,关于for的使用如下
1.for (i in 1 until 100 )相当于 [1,100)的区间类,步长是1,相当于i ++
2.for (i in 1 until 100 step 2)
指定步长是多少,step跟上步长
3.for (i in 0..100)
区间[0,100],步长为1
step:是步长,在for循环中是特有的
结果如下:
22、参数泛型
在kotlin中,对参数类型的泛型要求蛮高的,即使,实参和形参的泛型不一致都会提示错误
所以在使用过程中,定义参数泛型后,实参和形参一定要匹配,否则很容易报错,这种错误和java中不一样,java只要容器一样,参数是默认类型的。
有时候在设置泛型一定要格外小心,避免新手排查问题无法定位到
解决:
如果你是在不想预先定义好,可以如下:
class FanxingDemo(var map:HashMap <*,*>) {
fun log(){
map.keys;
}
}
fun main() {
var map=HashMap<Any,Any>()
var demo=FanxingDemo(map)
}
形参指定*
var map:HashMap <*,*>
实参:
var map=HashMap<Any,Any>()
或者
var map: HashMap<*, *> = HashMap<Any?, Any?>()或者先定义,后初始化
var map: HashMap<*, *> map= HashMap<Any?, Any?>()
参数put的时候需要注意:
key和value要转换成Nothing,否则报错
map.put("one" as Nothing, 1 as Nothing); map.put("two" as Nothing, 1 as Nothing);
但是这种在编写没有问题,但是在执行的时候会报错
Nothing是Void类型,所以String不能转Void
所以在实战中,实参的泛型不要通过*来修饰,Demo如下
正确:
var map: HashMap< Any, Any> = HashMap<Any, Any>()
var key = Any()
map.put("123",123)
23、Bean的封装
在kotlin中,类有两种写法
1.正常有类体的
class MyBean() { var name: String = "" var age: Int = 0 var map: HashMap<*, *> = HashMap<Any?, Any?>() }
2.无类体,只通过默认构造器完成
class MyBean(var name: String = "", var age: Int, var map: HashMap<*, *> = HashMap<Any?, Any?>())
3.空类
class MyBean()
由于kotlin所有的变量都提供了set()和 get()方法,所以如果做bean来使用,无法处理结果,直接用第二种即可。
第二种写法也常和Gson、Json配合解析使用
正常使用如下:
var bean = MyBean("zhangshan", 100, map)
MyLog.log(bean.name + "," + bean.age + "," + bean.map.toString())
24、String的快捷内置API的使用
val value="123"
value.toFloat()
value.toInt()
value.toDouble()
value.toLong()
value.toCharArray()
value.toList()
value.toBigDecimal()
value.toByte()
value.toShort()
value.toBoolean()
kotlin的String类型,已提供了好多快捷的转换。我们可以直接使用,这样不用我们自己去换了
25、异常的抛出方法
kotlin中有两个异常抛出的方法
1.在方法体中抛出
try {
Thread.sleep(122)
} catch (e: Exception) {
}
2.在方法外面抛:通过@Throws跟上异常类
@kotlin.jvm.Throws(Exception::class)
fun testException() {
Thread.sleep(12)
}
26、Synchronized同步锁使用
kotlin的同步锁用两种用法
第一种:变量默认方法加锁
我们都知道,kotlin的变量默认有set和get方法。所以同步锁在锁变量的时候,可以单独对变量的set和get进行加锁。
Synchronized
class TestSyn { @set:Synchronized @get:Synchronized var instance = TestSyn() }
加锁格式:@+(set、get)+ :+Synchronized
field域不能直接对变量加锁,因为变量不支持,锁只支持方法
错误写法:
@Synchronized var instance = TestSyn()
第二种:自定义方法加锁
自定义方法加锁直接在方法名上方插入,如下:
@Synchronized fun test(){ }
格式 :@Synchronized
日常我们了解这两种方式已够开始使用
27、Volatile:原子一致性
这些都是属于jvm的,直接@引入即可
@Volatile var a: Int = 10;