自Kotlin被google官方认可之后,Android界对Kotlin的热度便持续上涨,在了解了它的优势后,作为一名与时俱进的Android开发工程师,是时候有必要来学习Kotlin了。
Kotlin有何优势?
- 代码的大幅度精简
- 100% 兼容 Java 代码
以上两点是我认为Kotlin的最大优势,目前java 的主流开发工具基本都支持了java转Kotlin,因此转换基本不需要什么成本,就算在使用过程中遇到坑,完全可以用java来写,无隔阂的运行,一开始不熟悉的时候还可以用java编写后转成Kotlin,不断对比就会逐渐熟悉。(ps:一个好的IDE对学习也是非常有帮助,在写Kotlin的代码时android studio 经常会提醒我有简化的写法)
Kotlin基础语法
对比学习
kotlin中一切皆为对象 ,没有像java一般的基本数据类型,数值类型为:Int, Float, Double等都是对象(类似java的包装类);函数也是对象,可作为参数和返回值。
定义常量与变量
可变变量定义:var 关键字
var <标识符> : <类型> = <初始化值>
不可变变量定义:val 关键字,只能赋值一次的变量(类似Java中final修饰的变量)
val <标识符> : <类型> = <初始化值>
类型检测及自动类型转换
我们可以使用 is 运算符检测一个表达式是否某类型的一个实例(类似于Java中的instanceof关键字)。
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// 做过类型判断以后,obj会被系统自动转换为String类型
return obj.length
}
}
- Kotlin中的Any = Java中的Object
类型强转
类型强转在Android中非常常见,如:
GridView gridView = (GridView) findViewById(R.id.grid_view);
而Kotlin中使用as关键词:
val recyclerView = findViewById(R.id.recycler_view) as RecyclerView
字符串模板
$ 表示一个变量名或者变量值
$varName 表示变量值
${varName.fun()} 表示变量的方法返回值:
var a = 1
// 模板中的简单名称:
val s1 = "a is $a"
a = 2
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, but now is $a"
static : companion object
java中static表示类相关的,生命周期于类相同,Android中经常会用来声明一些static常量和工具类的static方法
public static final int A = 1;
public static final String STR = "abc";
public static int getInt() {
return A;
}
在Kotlin中则没有static关键字,取而代之的是companion object (伴生对象)
companion object{
val A = 1 //val表示不可变,类似java的 final
val STR = "abc"
}
在Android中常见的utils类(只有静态方法)可以变成这样:
class Utils private constructor() {
init {
throw AssertionError()
}
companion object {
fun staticFun() {/*do someting*/
}
}
}
当然,这是java思维下Kotlin转变,实际上用Kotlin实现工具类还有其他的方法,其一是使用object:
object Utils {
/** 获得状态栏的高度 */
@JvmStatic
fun getStatusHeight(context: Context): Int {
var statusHeight = -1
try {
val clazz = Class.forName("com.android.internal.R\$dimen")
val obj = clazz.newInstance()
val height = Integer.parseInt(clazz.getField("status_bar_height").get(obj).toString())
statusHeight = context.resources.getDimensionPixelSize(height)
} catch (e: Exception) {
e.printStackTrace()
}
return statusHeight
}
}
注意@JvmStatic
,代表了在Java调用时也可以像static方式那样使用:Utils.getStatusHeight()
,如果不加这个注解的话在java中调用Utils.INSTANCE.getStatusHeight()
。
还有另一种方式顶层函数,也是Kotlin实战这本书中推荐的方式,不需要创建类,而是直接创建一个.kt文件定义函数(方法):
@file:JvmName("ScreenUtil")//定义java中的类名
package com.huburt.library.util
import android.content.Context
/**
* Created by hubert
*
* Created on 2017/11/2.
*/
fun getStatusHeight(context: Context): Int {
var statusHeight = -1
try {
val clazz = Class.forName("com.android.internal.R\$dimen")
val obj = clazz.newInstance()
val height = Integer.parseInt(clazz.getField("status_bar_height").get(obj).toString())
statusHeight = context.resources.getDimensionPixelSize(height)
} catch (e: Exception) {
e.printStackTrace()
}
return statusHeight
}
Kotlin中可以直接使用该方法,会自动导入import com.huburt.library.util.getStatusHeight
,java中使用还是会像使用static方法一样ScreenUtil.getStatusHeight(this);
,如果没有定义java类名@file:JvmName("ScreenUtil")
的话,会默认使用.kt的文件名+Kt,如这里我是放在ScreenUtil.kt文件中,使用就是ScreenUtilKt.getStatusHeight(this);
既然Kotlin没有static关键字,java中较多使用的
单例模式
class Singleton {
private Singleton() {
System.out.print("This is a singleton");
}
private static class Holder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return Holder.instance;
}
}
在Kotlin中也需要作出改变
class Singleton private constructor() {
init {
println("This ($this) is a singleton")
}
private object Holder {
val INSTANCE = Singleton()
}
companion object {
val instance: Singleton by lazy { Holder.INSTANCE }
}
}
构造
可以看到Kotlin的constructor(构造器)位置与java中不同。
Koltin 中的类可以有一个 主构造器,以及一个或多个次构造器,主构造器是类头部的一部分,位于类名称之后:
class Person constructor(firstName: String) {}
如果主构造器没有任何注解,也没有任何可见度修饰符,那么constructor关键字可以省略。
class Person(firstName: String) {}
主构造器中不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀。
class Person constructor(firstName: String) {
init {
System.out.print("FirstName is $firstName")
}
}
注意:主构造器的参数可以在初始化代码段中使用,也可以在类主体n定义的属性初始化代码中使用。 一种简洁语法,可以通过主构造器来定义属性并初始化属性值(可以是var或val):
class People(val firstName: String, val lastName: String) {
//...
}
如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字:
class Person() {
var parent: Person? = null
constructor(parent: Person) : this() {
this.parent = parent
}
}
对象
Kotlin中创建对象不需要 new
,只需要调用构造器如:var person = Person()
匿名内部类
Android中对于匿名内部类的使用也非常多,典型的setOnClickLsitener
,而在Kotlin中使用object关键字实现匿名内部类:
button.setOnClickListener(object: OnClickListener {
override fun onClick(v: View?){}
})
javaBean
javaBean也是Android开发中非常常见的,用于封装数据,这是一个基本的网络结果实体类
public class BaseEntity<T> {
private int code;
private String message;
private T data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
用Kotlin去声明的话:
class BaseEntity<T> {
var code: Int = 0
// set //默认实现
// get
var message: String? = null
var data: T? = null
}
不要认为这只是java中public声明的属性,这些属性都有默认实现的getter/setter,如果不想某个属性被修改(只读)只需要在set前声明 private
。
还有另外一种更简便的写法,Kotlin中定义了一种数据类,可以创建一个只包含数据的类,使用关键字为 data:
data class DataClassExample (val x: Int, val y: Int, val z: Int)
编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:
- equals() / hashCode()
- toString() 格式如 "User(name=John, age=42)"
- componentN() functions 对应于属性,按声明顺序排列
- copy() 函数
如果这些函数在类中已经被明确定义了,或者从超类中继承而来,就不再会生成。
为了保证生成代码的一致性以及有意义,数据类需要满足以下条件:
- 主构造函数至少包含一个参数。
- 所有的主构造函数的参数必须标识为val 或者 var ;
- 数据类不可以声明为 abstract, open, sealed 或者 inner;
- 数据类不能继承其他类 (但是可以实现接口)。
var message: String?
是否对这个?
感到奇怪。其实这是表示这个属性可能为null,在使用的时候需要进行处理。
NULL检查机制
在Android中使用变量前我们需要对其进行空判断避免空指针,这样往往带来带来大量的工作,这些空判断代码本身没有什么实际意义,并且让代码的可读性和简洁性带来了巨大的挑战。除此之外,我们经常会忘记判空!!!
Kotlin 为了解决这个问题,它并不允许我们直接使用一个可选型的变量去调用方法或者属性。对于声明可为空的参数,在使用时要进行空判断处理,有两种处理方式,字段后加!!像Java一样抛出空异常,另一种字段后加?可不做处理返回值为 null或配合?:做空判断处理
//类型后面加?表示可为空
var age: String? = "23"
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1 //等价于 if (age != null) age.toInt() else -1
方法(函数)
java中的方法:
public int funName() {
return 1;
}
而在Kotlin函数定义使用关键字 fun,参数格式为:参数 : 类型
fun sum(a: Int, b: Int): Int { // Int 参数,返回值 Int
return a + b
}
可变长参数函数
java中使用...
表示可变参数
函数的变长参数可以用 vararg 关键字进行标识:
fun vars(vararg v:Int){
for(vt in v){
print(vt)
}
}
todo
之前用java开发Android的时候,对于一些放在之后实现的功能,为了防止忘记,会加上//TODO
,以方便自己后续记得在此处补上实现。而在Kotlin中,输入todo,IDE提示的是一个TODO("xxx")
的函数,功能类似,只是在调用此处方法的时候回抛出一个异常:
kotlin.NotImplementedError: An operation is not implemented: xxx