About Kotlin(1)
因为是从Java的角度来学习Kotlin,在Java中,类作为第一等公民。故学习Kotlin,也先从其的类开始。
Kotlin中的类
参考文章
普通的类 class
Kotlin中没有添加修饰符直接以class开头的都是final类。final类的意义在于JVM可能存在的优化(常量和方法内联)和防止继承的滥用。
《Effective Java》在第17条说,要么为继承而设计,并提供文档说明,要么就禁止继承。
构造方法
Kotlin中一个类有一个primary的构造方法和可以多个的secondary的构造方法。
//如果一个类,没有类体,甚至连花括号都可以省略
class Empty
//如果主构造方法没有注解/修饰符。则constructor可以直接省略
class Person constructor(firstName:String){}
//省略的写法
class Person(firstName:String){}
//注解
class Person @Inject constructor(firstName:String){}
//修饰符
class Person private constructor(firstName:String){}
//第二构造器
class Person(val name:String){
//可以只用this,来指代第一个构造器
constructor(name:String,parent:Person):this(name){
//方法中,this同样可以用来指代对象
parent.childeren.add(this)
}
}
初始化
//如果使用默认构造方法的话,初始化代码不能现在方法内
class Customer(name:String){
//使用init代码块来完成主构造函数的中的初始化
init{
logger.info("Custormer initialized with value ${name}")
}
}
//另外,主构造函数传入的值,可以直接在类的变量中使用
class Customer(name:String){
//属性变量可以直接使用构造函数中的变量
val cumtomerKey = name.toUpperCase()
}
//通过这样,可以将主要构造方法中的参数直接变成类的属性.还可以直接写默认值
//在JVM上面会自动生成无参构造方法
class Customer(val firstName:String="",var lastName:String,var age:Int){
}
Kotlin中可以提供默认值的方式,解放了手写的代码量,及构造器模式Builder Pattern!
Effective Java的条目2讲述了如何有效地使用构造器模式(Builder Pattern)当Java构造函数有很多可选参数时,代码将变得冗长,可读性差且容易出错。构建这样的对象需要写很多代码。
对应的Open类
对应上面的类,kotlin中的open类就是能够被继承的了。必须在类声明的时候使用open关键字。Kotlin社区有人对这个 “默认的final” 设计很不满。Kotlin论坛对此进行了激烈的讨论。后来,在Kotlin 1.1 beta版中提供了一个编译器插件,可以让class默认是open。
//Kotlin中所有类的父类是Any,类似于Object,不过Any不输于java.lang.Object,其有三个方法
//这里的operator是操作符重载,在Kotlin中的符号中会说明
public open operator fun equals(other:Any?):Boolean
public open fun hashCode():Int
public open fun toString():String
覆盖方法
override的强制使用。
Java 1.5 中加入了注解(annotation),其中最重要的一个是重写(override),表示这个方法是对超类中该方法的重写。基于书中条目36,应该尽量使用这个可选注解以避免一些恶心的bug。比如当你以为你重写了超类的方法但其实并没有时,编译器会抛出一个错误。不过如果你记得加上了override注解的话就没事。
在Kotlin中,override不是可选的注解而是强制关键字。所以由此引发的bug就不会再有了,编译器会提前警告你。Kotlin把这些事清楚的展现出来。
open class Base{
open fun v(){}
fun nv(){}
}
class Derived():Base(){
//override 关键词的使用
override fun v(){}
}
//如果使用相同的两个方法。则可以用super类指代不同的父类的方法
interface B{
fun v(){print("B")} //interface也能够写方法的实现
}
class DoubleImpl():Base(),B{
override fun v(){
//调用 Base.v()
super<Base>.v()
//调用B的方法的话
super<B>.v()
}
}
//这里还有个需要注意的是,属性的重写也需要用override
open class Foo{
open val x:Int get{...}
}
class Bar1:Foo(){
override val x:Int=...
}
数据类
所谓的数据类,就是Java的Pojo,除了字段外,基本不会写其他的方法。
//在Kotlin中,可以使用data关键字
data class User(val name:String,val age:Int)
自动生成的函数
编译器会自动的从主构造函数中根据所有声明的属性提取以下函数
-
equals()/hashCode()
-
toString
-
componentN() functions 对应属性。按照顺序排列
-
copy 函数。
//关于深拷贝的例子。有时候只是需要修改部分的属性 val java = User(name="Jack",age=1) val oderJack = java.copy(age=2) //对于上面User类。copy的实现就如下 fun copy(name:String=this.name,age:Int=this.age)=User(name,age)
确保有意义
为了确保数据类是一致的并且有意义的。该类必须有至少一个参数,不能其他的类型。
如果生成的类需要无参构造函数,则所有的属性必须指定默认值
解构声明赋值
注意,这个解构声明是专属于data class的
data class Person(var name:String="lisi",var age= 12)
val person = Person("kobe",30)
//将对象解构
val (name,age)=person
//内部实现为
val name = person.component1()
val age = person.component2()
var (name,_)=Person() //不需要使用变量时,可以用下划线替代
下面这个是一个更直接的例子
//因为有了ComponentN方法,才可以使用解构函数
class Pair<K,V>(val first:K,val second:V){
operator fun component1():K{
return first
}
operator fun component2():V{
return second
}
}
//vararg 是可变参数的意思
fun main(vararg args:String){
val pair = Pair(first=1,second="One")
val (num,name)=pair
println("num=$num,name=$name")
}
网友评论