目前Android开发中,分为两派,Java派和Kotlin派,Java是宇宙开发第一语言,地位一直处于领先位置,但是Java会出现一个空指针的情况,所以kotlin的出现,填补了java的一些缺陷,但是也导致一些人习惯了Java语言,在转kotlin的时候,会经常写成java字段。所以我接下来把kotlin在开发过程常见的关键字和一些用法进行介绍,以及一些错误如何处理,看完基本就能正常上手写kotlin项目。
似曾相识,就是不会。看完恍然大悟。接下来对高频用法和高频关键字进行介绍
var a:Int=10,
a=20
val b:Int=10
b=20//这行报错
var chidl:Child
如果直接使用报错
var child=Child()直接new一个新的
!!:如果有空指针会被抛出
?:如果当前对象是空指针,将不会执行后面的操作
如下:
1.var child:Child?=null
这种表示child为null,
如果你直接写成
var child:Child=null
直接报错
变成的初始化可以这样
var child:Child= Child() 声明成Child的类型,然后初始化 var child1=Child()
直接初始化成child类型
在kotlin中,数组分为可变和不可变,可变是可以添加值,不可变就是声明后,就不能再add进去。
mutableListOf<>() mutableMapOf<>() mutableSetOf<>()
在这边,var和val对对象的修饰不是那么明显,即使你申请为val,可变数组还是可以操作的
我们在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") } }
单利在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!! } } }
const是修饰val的,val是只有读权限,如果用const来修饰,那就变成了一个常量,const 只允许在top-level级别和object,也就是是说当前类必须是通过object修饰的,否则不能使用
1.正确写法
const val a:Int = 0;
2.错误写法
const val a:Int;
3.const的出现:只能出现在静态类或者静态模块中,否则报错
- interface MyInterface {
-
- public fun change(msg: String): String;
-
- public fun add(a: Int, b: Int): Int {
- return a + b
- }
-
- }
也是通过关键字interface 来修饰
写法没有啥区别,唯独有争议的地方就是方法可以有方法体,有方法体只能当方法使用
类和的继承和接口都是通过":"实现的
如果接口中定义的方法有方法体,在接口继承将不会需要重写,已被定义为方法了,可以直接调用,也可以重写
- 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))
-
- }
1.直接和java写法一致:
- demo.setInterface(object : MyInterface {
- override fun change(msg: String) {
-
- }
- })
-
-
2.lambda表达式:
无参:直接{}在空号内写回调
demo.setOnclick { }
有参:直接将参数名按顺序列出来,指向方法体->
demo.addOnDestinationChangedListener {controller,destination,arguments-> toast("${destination.displayName}") }
非继承类构造器
- 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() {
-
- }
-
- }
和单类一样,只是要传参给父类。其他写法和非继承一样
lateinit 关键是需要对变量进行初始化,切变量属性是private类型,切用来修饰var
在Android开发过程中,比较常见的就是lateinit var来定义view
lateinit var text=findViewById(id)as TextView
这种修饰是申请变量可以为null,一般简单类型不适合
lateinit var age:Int
同样,这样修饰过的变量不能去修改set()和get()方法
lateinit var mp1:HashMapvar mp2:HashMap
as在kotlin中作为类型的强制转换,Java中 int a=(int) 100.123f
对象类型
a=b as Int
常见的view初始化如下:
var text=findViewById(id) asTextView
我们如果在kotlin中构造器中有对象变量,需要引用构造器变量,无须将构造器变量赋值到类的内部变量,可以直接通过var修饰完直接引用
class DataMapConst(var map:HashMap?)
- class DataMapConst(var map:HashMap
?) { -
- lateinit var mp1:HashMap
-
- 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
?=null - map= HashMap()
- DataMapConst("ssss")
- var demo=DataMapConst(map);
- map.put("one","1")
- map.put("two","1")
- map.put("three","1")
- demo.Child().log()
-
-
- }
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技术。同时,也破坏了数据原有的安全性,谨慎使用。
companion只能修饰object,且该object必须是内部类静态模块。
常见的用法如下
- class TestSing {
-
- companion object {
- //类的静态模块
-
-
-
- }
- }
1.修饰类为静态内
2.和companion搭档,companion object修饰非静态类的静态模块
如果类为静态类,就不能这样使用,因为静态类,里面的方法和熟悉都可以直接使用,再修饰成静态模块就会报错。
所以 :只能出现在非静态类中
companion object{
}
这种用法最为非静态类中的静态模块
kotlin中的类和方法都是无法直接被继承或者重写,类似Java中被修饰了,但是可以通过open来修饰就变成了开放型类和方法。
- open class Base {
-
- open fun log(){
-
- }
- }
-
- class TestBase :Base(){
-
- override fun log() {
- super.log()
-
- }
-
- }
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可以作为了解,不做参考
如果类中嵌套类,我们常叫成为内部类,在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()
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。
在kotlin中,类型的判断是 is,和java instanceof一样
- var b=10
- if (b is Int)
- {
- MyLog.log("Int")
- }
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循环中是特有的
结果如下:
在kotlin中,对参数类型的泛型要求蛮高的,即使,实参和形参的泛型不一致都会提示错误
所以在使用过程中,定义参数泛型后,实参和形参一定要匹配,否则很容易报错,这种错误和java中不一样,java只要容器一样,参数是默认类型的。
有时候在设置泛型一定要格外小心,避免新手排查问题无法定位到
如果你是在不想预先定义好,可以如下:
- class FanxingDemo(var map:HashMap <*,*>) {
-
- fun log(){
- map.keys;
- }
- }
-
-
- fun main() {
- var map=HashMap
() -
- var demo=FanxingDemo(map)
-
- }
形参指定*
var map:HashMap <*,*>
实参:
var map=HashMap
() 或者
var map: HashMap<*, *> = HashMap() 或者先定义,后初始化
var map: HashMap<*, *> map= HashMap()
参数put的时候需要注意:
key和value要转换成Nothing,否则报错
map.put("one" as Nothing, 1 as Nothing); map.put("two" as Nothing, 1 as Nothing);
但是这种在编写没有问题,但是在执行的时候会报错
Nothing是Void类型,所以String不能转Void
正确:
var map: HashMap< Any, Any> = HashMap()
var key = Any()
map.put("123",123)
在kotlin中,类有两种写法
1.正常有类体的
class MyBean() { var name: String = "" var age: Int = 0 var map: HashMap<*, *> = HashMap() }
2.无类体,只通过默认构造器完成
class MyBean(var name: String = "", var age: Int, var map: HashMap<*, *> = HashMap())
3.空类
class MyBean()
由于kotlin所有的变量都提供了set()和 get()方法,所以如果做bean来使用,无法处理结果,直接用第二种即可。
第二种写法也常和Gson、Json配合解析使用
正常使用如下:
-
- var bean = MyBean("zhangshan", 100, map)
-
- MyLog.log(bean.name + "," + bean.age + "," + bean.map.toString())
- 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类型,已提供了好多快捷的转换。我们可以直接使用,这样不用我们自己去换了
kotlin中有两个异常抛出的方法
- try {
-
- Thread.sleep(122)
- } catch (e: Exception) {
-
- }
- @kotlin.jvm.Throws(Exception::class)
- fun testException() {
- Thread.sleep(12)
- }
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
日常我们了解这两种方式已够开始使用
这些都是属于jvm的,直接@引入即可
- @Volatile
- var a: Int = 10;
在java中,如果需要在一个String文本中占位一个坑,然后通过数据替换,常见的做法就是MessageFormat来处理。在kotlin中占位可以直接在text文本中使用
- var name="zhangshan"
- val text="我的名字叫${name}名字长度${name.length}"
占位符的使用,可以简化我们正常的拼接和formate转换。
在kotlin中可以参数需要通过vararg来修饰,
String... arry
vararg var arry:String
class Test( vararg var arry:String)
let的是kotlin特有的条件,通过自身一些条件,执行let体
if(entity!=null){
initView();
}
- entity?.let{
-
- initView()
-
- }
for(object item:arry)
arry.forEach { it }
我们正常遍历Java如下:for(int a=0;a<100;a++),kotlin不支持这种写法,kotlin的for搭配in使用
所以我们正常使用
for(index in 0..100)
区间在[0,100],因为数据小标都是size-1,如果我们不小心导致数组越界
所以在kotlin集合有一个字段已处理了:indices
- public val Collection<*>.indices: IntRange
- get() = 0..size - 1
arry.indices=arr.size-1
我们在遍历采用indices属性,就可以避免数组越界的问题