kotlin
kotlin 是一门可以运行在jvm上的语言, 兼容java的代码。 和java的谨慎保守不同, kotlin在编码规范上进行了大量的改进, 拥有了许多java语言不支持的特性, 并且kotlin还是安卓开发的优先语言, 所以值得学习。
val 和var
kotlin 声明属性变量可以使用这两个关键字来设置, 其中
val a:Int = 12
var b:Int =11
上面的定义方式类似于java中的。
final Integer a=12;
Integer b=11;
并且kotlin 中默认所有的属性在声明的时候必须指定它的值, 不能像java样使用默认值,这个特性极大的避免了空指针异常。 当然这个必须指设值也不是绝对的, 你可以声明不设置:
var a:Int? =1
a =null //这也不会报错
var b:Int = 2
b= null //这个会报错
kotlin的变量默认必须设置的值的要求,同样在方法的参数和返回值上是适用的, 所以可以从源头上极大的避免空指针异常。
类型推导
上面的变量的定义都适用了 :Int
的方式声明了类型。 但是kotlin 实际上是支持类型推导的, 我们可以省略这个类型的声明;
fun main(args: Array<String>) {
var a = 21 // 这里没有声明什么类型
println(a)
a = "xxx"; //这里会报错
println(a)
}
从上面的例子看到a变量的定义没有写上类型。 但是依然没有问题,这是因为从后面的值推导出来了类型, 所以可以不写。
但是不用写类型,并不代表没有类型, kotlin仍旧是一个强类型的语言, 与js和python 是不同的, 所以后面如果字符串再赋值给a会报错。
支持字符串模板
fun main(args: Array<String>) {
val name = "sweettea"
println(" hello - ${name} - $name - ${name.length} ")
}
运行结果如下:
hello - sweettea - sweettea - 8
判等
在java中 等于
是一个很让人头疼的操作, 并且java中对于字符串相等判断还有享元缓存问题和Null问题, 很容易出错。 并且对于Integer等其他类型的相等判断也是一样的。 所以逼得java程序员对于相等的判断一律使用Objects.equals(a,b)
- 结构相等 通过操作符
==
来判断两个对象的内容是否相等 - 引用相等 通过操作符
===
来判断两个对象的引用是否一样,与之相反的判断操作符是!==
- 如果比较的是在运行时的原始类型,比如 Int ,那么
===
判断的效果也等
价于==
class interface 和 init
interface Fly{
fun fly()
}
class Bird(val name: String):Fly {
init {
println(name)
}
override fun fly() {
println("i am $name i can fly")
}
}
fun main(args: Array<String>) {
val bird = Bird("sparrow")
bird.fly()
}
上面的代码运行结果如下:
kotlin中的接口定义和java相似。 class的定义与java 有点差别, class的构造方法是直接写在类名后面的, 并且内部可以在init
代码块中协商构造方法的逻辑。
并且class中的构造方法的参数会直接作为类的成员变量使用。
kotlin也支持多构造方法,但是必须有一个主构造方法, 其他的都是从构造方法, 主构造方法就是在类名后面定义的。
从构造方法可以单独定义, 从构造方法必须 '继承'
主构造方法。 (找不到准确的词形容,只能使用继承
)
interface Fly {
fun fly()
}
class Bird(val name: String) : Fly {
/**
* 在构造方法中要执行的逻辑, 只要调用了构造方法, 这里的代码就会执行,
* 可以定义多个init代码块, 多个代码块会按照定义的顺序执行
*/
init {
println("init $name")
}
/**
* 从构造方法
*/
constructor(age: Int, name: String) : this(name) {
println("age:$age name:$name")
}
override fun fly() {
println("i am $name i can fly")
}
}
fun main(args: Array<String>) {
val bird = Bird(12, "sweettea")
bird.fly()
val bird2 = Bird("sweettea2")
bird.fly()
}
上面的代码执行结果:
init sweettea
age:12 name:sweettea
i am sweettea i can fly
init sweettea2
i am sweettea i can fly
多继承问题
java是不支持多继承特性的,但是现实中总是需要一些多继承
的支持, 这个时候可以使用java的接口多继承, 内部类两种方式来实现。 当然kotlin也不支持多继承, 他也可以使用这两种方式类支持多继承
. 并且kotlin还很方便的使用委托来代替多继承。
接口多继承
与java的多继承类似, 就是实现类实现多个接口。 没什么好说的。
内部类解决多继承
继承的目的就是为了获得父类的方法和属性并且可以扩展方法和属性, 多继承就是为了获得多个父类的方法和属性。 但是获取多个父类的方法和属性不一定非要使用继承。
内部类和正常类几乎一样, 他也可以继承一个父类, 并且内部类完全可以定义成private
, 这样调用者完成感知不到内部类的存在, 如果A类需要继承两个父类, 完成可以使用一个内部类来继承一个父类, A类再继承一个父类。 这样A相当于就拥有了两个父类的属性和方法。
open class Horse { //马
fun runFast() {
println("I can run fast")
}
}
open class Donkey { //驴
fun doLongTimeThing() {
println("I can do some thing long time")
}
}
class Mule { //骡子
fun runFast() {
HorseC().runFast()
}
fun doLongTimeThing() {
DonkeyC().doLongTimeThing()
}
private inner class HorseC : Horse()
private inner class DonkeyC : Donkey()
}
上面的示例代码就是使用了两个内部类来实现 骡子 马的特性, 又有驴子的特性的。
委托代替多继承
除了内部类的方式, 还可以通过委托的方式实现类似继承的特性, 委托更像是静态代理或者装饰器模式。
方便的数据类
java中定义一个DTO类, 可能需要写一堆的代码, 什么get set方法, toString等等, 虽然Lombok 也很方便,但是lombok毕竟是第三库, 并不是语言支持的。
而kotlin 只要一行代码就可以支持这样的类。
data class User(val id:Int, val name:String)
并且kotlin 还有copy和componentN方法, 这两个方法用起来也非常方便, 具体用法就不展开了。
static 和 object
java的static定义的方法和属性是属于类的, 并不是某个具体的对象, 并且全局只有一份(可以修改),我们经常使用这个特性来实现单例模式。 但是kotlin 舍弃了static,使用了伴生对象的特性来代替。
companion object
就定义了一个伴生对象
class Prize(val name: String, val count: Int, val type: Int) {
companion object {
val TYPE_REDPACK = 0
val TYPE_COUPON = 1
fun isRedpack(prize: Prize): Boolean {
return prize.type == TYPE_REDPACK
}
}
}
fun main(args: Array<String>) {
val prize = Prize("红包", 10, Prize.TYPE_REDPACK)
println(Prize.isRedpack(prize))
}
单例模式
java的单例模式需要我们自己写一堆的代码进行判断和加锁来保证全局唯一, 但是kotlin已经在语言层面支持单例模式了
object OnlyOneUser {
var name= "xzc"
var id=12
}
使用object关键字, 不用定义构造方法, 不用自己实例化, kotlin 就会自动进行实例化,并且保证全局唯一。