Android为啥要从Java转向Kotlin
Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,被称之为 Android 世界的Swift,由 JetBrains 设计开发并开源。苹果公司已经在用Swift语言替代Object-C语言,Google也找到了替代Java的语言,也就是JetBrains公司(Android Studio也是用该公司的Intelli J改的)主推的Kotlin。现在将kotlin作为了编写Android的官方语言,而后会有越来越多的项目使用Kotlin。Kotlin有了强有力的亲爸JetBrains公司和Google这个干爸,自然被捧在手心了。
Kotlin在编写代码时有如下优势:
- 代码简洁高效
- Android Jetpack 与其他库中的 Kotlin 支持
- 可与 Java 的一起使用
- 空指针安全
同样,kotlin支持函数式编程、支持lambda表达式、流式API...,此处略去一万字。总之,Java是上世纪的编程语言了,当你学过了Kotlin之后 ,你会发现,之前写过那么多的代码都是在浪费生命。
语法基础
变量
var age: Int = 0
var num = 20
val TYPE: Int = 100
var name: String = ""
写法形如:var str: String
用var或val声明,前面是参数名,后面是参数的类型,中间用:分割
var即英文“variable”的意思,声明成一个可变变量。val即英文“value”的意思,申明的是一个不可变变量,对应的java里面的final。
kotlin中有优秀的类推导机制,age声明了变量类型,num没有声明变量类型,但是依然可以通过,是由Kotlin自动推导了类型。
常量
const
如果只读属性的值在编译期是已知的,那么可以使用 const 修饰符将其标记为编译期常量,相当于java中的public static final修饰。
//const val 需与 companion object 搭配使用
companion object {
const val VERSION = 1
}
空指针检查
在Kotlin里,可以用“?”表示可以为空,也可以用“!!”表示不可以为空。
给变量加上?标识,会通告所有使用该变量的地方,必须给出为空的补救措施。
var info: String? = null
println(info?.length) //第一种补救:如果info为null,就不执行后面的.length代码
println(info!!.length) //第二种补救:这里如果为null,我自己负责info,会报出空指针,这种处理需慎用
if (info != null) { //第三种补救措施,如下这种同java写法
println(info.length)
}
println(info?.length ?: "空数据") //第四种补救措施,如果真的为null,则改为返回"空数据"
第二个检查方式是let函数,?.当然能解决大部分问题,但是user每使用一次?.相当于都加上了代码if(user != null),实际上在同一个函数中,我们只需要做一次非空判断就行了,这就是我们的let函数的作用,在代码块内容用it代替该对象。
user?.let {
it.login()
it.logout()
}
可见性修饰符
在 Kotlin 中有这四个可见性修饰符:private、 protected、 internal 和 public。 如果没有显式指定修饰符的话,默认可见性是 public。
internal:在本模块内可见
字符串拼接
将变量拼接在字符串之内,用${}来包裹变量即可
fun StringFormat(title: String) = {
"这里是拼接字符串${title}"
}
字符串自动换行:
val content = """
哈哈哈哈
呵呵呵呵
嘿嘿嘿嘿
""".trimIndent()
值比较、赋值:
println(name1.equals(name2)) //同java的equals
println(name1 == name2) //同equals作用,比较值的相等,这里为true
println(name1 === name2) //比较地址是否相等,即比较是否为同一个对象,这里为false
val name1 = "lili"
val name2 = "lili"
println(name1 === name2) //这里返回ture,因为字符串在常量池是复用一份的
将条件判断的结果赋值:
val num1 = 100
val num2 = 101
val max = if (num1 > num2) num1 else num2
when
用when关键字代替java中的switch使用,when作为判断,条件可为任意类型
fun whenUse(obj: Any, type: Int) {
when(obj) {
1 ->
""
in 2..5 ->
""
is String ->
""
else ->
""
}
}
val week = 5
val info = when(week) {
1 -> "星期一"
1 -> "星期二"
3 -> println()
4 -> 5
-1 -> TODO() //Nothing类型:表示未实现的功能,会抛出异常 public inline fun TODO(): Nothing = throw NotImplementedError()
else -> "" //返回String类型,info一定是String类型
}
//将when的返回值直接使用起来赋值:
val info2 = when(week) {
1 -> "星期一"
2 -> "星期二"
3 -> println()
4 -> true
in 2..5 ->
""
else -> { //必须要有else
//else 返回括号,info2就可以是任意类型 Any
}
}
Any
java中所有类的父类是Object,而Kotlin中所有类的父类为Any。
object
Kotlin中没有大写的Object了,而是有小写的object,表示单例。
class SingleTon {
object Holder {
var instance = SingleTon()
}
}
is
Kotlin中用is代码java中的instanceOf 来判断类型
fun charge() {
var type = ""
if (type is String) { //instanceOf
}
}
in使用
- in来检查一个值是否在一个区间内
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z' //检查字符是否为字母
- 检查list中是否有元素name
fun inList() {
val names = listOf("lala", "haha", "yaya")
if("lili" in names) {
}
}
takeIf,takeUnless函数
user.name.takeIf {
TextUtils.isEmpty(it)
}.let {
print(it)
}
user.name.takeUnless {
!TextUtils.isEmpty(it)
}.let {
print(it)
}
takeIf的闭包返回一个判断结果,如果为false时takeIf函数返回null;takeUnless与takeIf相反,为true时takeUnless函数返回null。
使用场景:只需要单个if分支语句的时候
优点:
可以配合其他作用域函数返回的结果,做出单向判断,保持链式调用
简化写法,逻辑清晰,减少代码量,代码更优雅
init
主构造函数里不能写代码,那我们怎么初始化这个类的代码呢?Kotlin则是提供了初始化模块,基本上就是用init修饰符修饰一个{},在类初始化时执行这段代码。
class User() {
init {
}
}
数组
创建数组
var names = arrayOf("小王", "小花", "小红", "小明")
var ages = arrayOf(22, 19, 18)
var array = arrayOfNulls<String>(5) //创建空数组
array.set(1, "哈哈") //赋值
数组遍历
fun arrayForEach() {
var names = arrayOf("小王", "小花", "小红", "小明")
for (i in 1..5 step 1) { //跳过第1步遍历
}
for (i in 0..names.size-1) { //遍历names数组,相当于java里 for (int i=0:i<names.length;i++)
}
for (i in names.indices) { //正序遍历
}
for (i in 1 until 10){ //为一个左闭右开的区间,打印1到9
print("$i")
}
for (i in names.size downTo 0) { //倒序遍历
}
for (i in names.reversed()) { //反转遍历数组
}
repeat(10) { //打印0到9
print(it)
}
names.forEach { //forEach函数遍历,Kotlin里的集合都自带foreach函数
}
names.forEachIndexed { index, s -> //带position的遍历
}
}
in的用法:val num = 1..10,表示变量num是一个[1,10]的区间。
step表步长,表示遍历间隔的个数。
类与对象
类构造函数
在 Kotlin 中的一个类可以有一个主构造函数以及一个或多个次构造函数。如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字。
class User constructor(name: String, age: Int, id: Int)
class User(name: String, age: Int, id: Int)
操作示例:
class User(val name: String) { //主构造函数
var age = 0
var sex = "man"
/**
* 有参次构造方法
*/
constructor(name: String, age: Int, id: Int): this(name) { //次构造函数,必须去调用主构造
}
constructor(name: String, sex: String): this(name) {
this.sex = sex
}
/**
* 无参次构造方法
*/
constructor(): this("暂无名字") {
}
init {
println("name is $name")
println("age is $age")
}
fun main() {
User() //次构造
User("小明") //主构造
User("小明", 18, 1) //次构造
}
}
创建对象
Kotlin中创建对象不再需要关键字new了,直接创建对象:
var user = User()
继承
在Kotlin中的类和方法默认是不可以被继承的,需要加上关键字open,才允许被继承。继承用":"表示,类和接口的继承都用":",类需要加()。
class VipUser : User(), Use {}
open class User {}
interface Use {}
内部类
class OuterClass {
val outerInfo: String = "outerInfo"
fun show() {
InnerClass().show() //外部类可以访问内部类
}
//加上inner关键字才是内部类,内部类才能和外部类相互访问,否则不是内部类而是嵌套类
inner class InnerClass {
fun show() = println("my outerclass info: " + outerInfo) //内部类也可以访问外部类
}
}
数据类
即为我们通常使用的bean类,一般在定义数据类的时候,我们需要手动实现这个类的equals()、hashcode()、toString()等方法,这些方法是必要的。但是加上data关键字后,该类会自动生成这些方法。
data class User(var name: String) {
}
单例类
在java中,单例的写法需要自己手动实现,比如用懒汉式还是饿汉式。但是在Kotlin中单例就非常简化了,只需要将class替换成object就可以了。
object SingleTonClass {
var params: String? = null
fun function() {
}
}
SingleTonClass.function()
SingleTonClass.params
使用单例时不需要再获取instance实例对象了,Kotlin已经在内部帮我们创建了一个单例的实例,直接使用类名调用它的属性和方法。
compaion object(伴生对象)
在java中static表静态变量,kotlin中取消了static,用compaion object来代替它的用法。
class Companionobject {
//伴生对象的由来,是kotlin没有static静态
//不管对象创建多少个,companion object只会初始化一次
companion object {
val info = "lili" //静态变量
fun showInfo() {
println(info)
}
}
fun test() {
//背后代码:生成了 Companionobject.companion类
println(info)
showInfo()
}
}
Kotlin 接口
//kotlin的接口
//接口里面的所有成员和方法和接口本身都是 public open的
//实现类不仅要重写接口的函数,还要重写接口的成员
interface IUSB {
var usbVersionInfo: String
fun insertUSB(): String
}
//成员重写写到实现类的构造方法中 方式
class Mouse(override var usbVersionInfo: String = "USB 3.0") : IUSB {
override fun insertUSB(): String = usbVersionInfo
}
//成员重写写到实现类中的方式
class KeyBorad: IUSB {
override var usbVersionInfo: String = "USB 3.0"
get() { //get/set 方法会调用代码块内容,并返回一个值
println("获取了 usbVersionInfo ${usbVersionInfo}")
return field
}
set(value) {
println("设置了 usbVersionInfo ${usbVersionInfo}")
field = value //field表示该 usbVersionInfo 属性
}
override fun insertUSB(): String = "keyboard $usbVersionInfo"
}
fun showInterface() {
val usb1 = Mouse()
usb1.insertUSB()
val usb2 = KeyBorad()
usb2.usbVersionInfo = "sfd" //使用属性调用get() 赋值属性调用set()
usb2.insertUSB()
}
JvmField JvmStatic
// JvmStatic JvmField注解使用
//用于 Java调用kotlin 属性和方法 兼容性 而产生的
//JvmField注解使用 将默认的private修改成了public访问符
//JvmStatic 将 companion object 中的 属性、方法能像kotlin一样能直接调用
class Personz {
@JvmField
val names = listOf("lala", "haha", "yaya")
//这个 names转成java就是如下代码, val修饰的 names属性成了 private final ,外部调用不到,而提供了一个方法获取
// 使用了 @JvmField注解,将该属性从private变成public,能直接获取val修饰的属性
/**
* public final class Personz {
* @NotNull
* private final List names = CollectionsKt.listOf(new String[]{"lala", "haha", "yaya"});
*
* @NotNull
* public final List getNames() {
* return this.names;
* }
* }
*/
companion object {
@JvmField
val name : String = "lala"
@JvmStatic
fun printlnPersonName() = println("名字: " + name)
}
}
//java中调用代码
public void getJvmFieldStatic() {
// new Personz().getNames(); //没加 jvmField的调用
List<String> names = new Personz().names; //加了 jvmField的调用
//没加 JvmStatic,java调用 Companion object需要加一层 Companion类来调用
// Personz.Companion.getName();
// Personz.Companion.printlnPersonName();
String name = Personz.name;
//加上 JvmStatic,就有了和Kotlin一样的调用效果,把函数写到了 Companion类 外面
Personz.printlnPersonName();
}
range符
fun range(number: Int) {
//range 范围
if (number in 10..59) {
println("不及格")
} else if (number in 0 .. 9) {
println("很差")
} else if (number in 60..100) {
println("及格")
} else {
println("分数不合法")
}
}
将现有Java文件转换为Kotlin文件
右键->Convert Java File to Kotlin File
我们可以将现有Java文件代码转换为Kotlin文件代码,而实现从java往kotlin上转移。可以针对单个文件,某个包,某个module,或者整个项目转为kotlin文件。
转换完成之后可能会有少量语法错误,需要手动修改一下。
将kotlin代码转为java
在Tools->Kotlin->show Kotlin ByteCode,点击Decompile转回类似Java代码。
教程参考:
https://www.bilibili.com/video/BV1kT4y1o7nP