1、类的定义
如何定义一个类。与Java类似的是,它需要一个class关键字.
class Track {
}
它有一个默认并且唯一的构造器,你只需要在类名后面加上参数:
class Track(json: String) {
//如果这个类没有任何的内容,则可以省略花括号
}
如果默认构造函数有注解或者可见性修饰符,那么需要加入构造器的关键词
class Track private constructor(json: String) {
}
当然你还可以增加其他的次要构造器。
两种写法
class Track {
constructor(json: String) {
}
}
class Track(json: String) {
constructor(json: String, id: Int): this(json) {
}
}
也就是说,如果该类没有申明默认构造体的时候,次要构造体可以用第一种写法。如果申明了默认构造体,所有的次要构造体都要引用默认构造体,类似Java中的super
那么构造函数的函数体写在哪里呢?
class Track(json: String) {
init {
JSONObject job = new JSONObject(json)
......
}
}
2、类的继承
Kotlin中默认的类都是基础继承至Any,类似Java中的Object。默认都是final状态,不可继承。当关键字是open或者abstract的时候,是可以继承的。
open class Track(json: String)
class TrackM(json: String, type: Int) : Track(json)
当我们只有当个构造器时,需要在从父类继承下来的构造器中指定需要的参数。类似super()
3、实例化
和Java的实例化类似,不同的是,Kotlin实例化对象省略了new关键字。
4、Data Class
在开发中,我们会创建一个model包,然后创建了很多model类,应用在各个页面。而这些model类,其实做的事情很少,声明属性,提供setter和getter方法。但是却写了非常多行的代码,甚至任何一个属性名的改变,都要去修改对应的方法,否则json的解析便有可能出错。在Kotlin中,提供了一个更加简介的写法。
data class User(val name:String,val age:Int)
同样的,它提供了一些默认的方法。
- equals()/hashCode() pair
- toString() of the form "User(name=John, age=42)"
- componentN() functions corresponding to the properties in their order of declaration
- copy() function
在使用这个类的时候,需要注意一下几点
- 主构造函数必须要有至少一个的入参(如果想要实现无参调用,只需要为所有入参设置初始值)
- 主构造的入参必须要标记成val或者var
- data class不能有abstract, open, sealed or inner修饰符
- 在1.1以前data class只能实现接口,之后的能实现其他类
关于copy函数
val vip1 = Vip(1, "Tom", 20)_____Vip(number=1, name=Tom, age=20)
val vip2 = vip1.copy(2)__________Vip(number=2, name=Tom, age=20)
val vip2 = vip1.copy(age = 40)___Vip(number=1, name=Tom, age=40)
也就是说,copy函数的入参是可选的,如果不指定参数名,默认是选择第一个参数。
关于componentN()函数
这个函数非常有趣和方便,主要用在拆解构造函数上面
val (a, b, c) = vip1
只要这么一些,原先vip1的三个入参,可以直接使用a、b、c。当然参数只能少不能多,并且是按顺序的,跟你的命名无关。这种语法成为解构声明。如果你在解构声明中你不需要某个变量,那么可以用下划线取代。
val (a, _, c) = vip1
5、Sealed Class
Sealed Class被用在描述限制继承的类,当一个值可能是某个优先集合的类型中,但不能是此外的任何类型的时候,我们就可以用到了。常用在switch case中。
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
fun eval(expr: Expr): Double = when(expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}
这里就不需要用到else了的判断语句块了,因为所有继承自Expr的类都覆盖到了。
6、范型
这里就不需要用到else了的判断语句块了,因为所有继承自Expr的类都覆盖到了。
class Box<T>(t: T) {
var value = t
}
val box = Box<Int>(0)
val box = Box(0)
如果编译器能够判断范型的类型,那么你无须指定类型,否则必须要给定你要的类型。
7、嵌入式类
class Outer { private val bar: Int = 1 class Nested { fun foo() = 2 }}
inner class可以调用外部类的成员,并持有它的一个object
class Outer { private val bar: Int = 1 inner class Inner { fun foo() = bar }}
匿名内部类,如果匿名内部类只有一个抽象函数,则可以使用lambda表达式简化写法
window.addMouseListener(object: MouseAdapter() { override fun mouseClicked(e: MouseEvent) { // ... } override fun mouseEntered(e: MouseEvent) { // ... }})
val listener = ActionListener { println("clicked") }
8、代理
8.1 代理类
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(a: Base) : Base by a
val b = BaseImpl(10)
Derived(b).print() // prints 10
区别于继承,它不用去实现接口。
8.2 代理属性
思考一个场景,我们希望监听某个属性是否调用了set/get方法,并给出响应。如果在Java中,我们可能需要在被监听的属性的settter和getter方法中去写代码,一个还好,十个、五十个,那就多了。有没有更好的接替思路呢?有的,代理,我们把settter和getter方法给别人做,这样的话,代理告诉我它调没调用这个方法。
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name} in $thisRef.'")
}
}
class Example {
var p: String by Delegate()
}
调用这个方法。
val e = Example()println(e.p)—————————> Example@33a17727, thank you for delegating ‘p’ to me!
e.p = "NEW" —————————> NEW has been assigned to ‘p’ in Example@33a17727.
一些标准的Kotlin代理:
- lazy 懒加载。第一次调用时候会调用get方法,如果初始化成功,那么下一次调用将会直接记住上一次的值,如果失败,则会继续初始化。
- Obeservable。用来观测属性值的改变过程。
- map。
9、单例模式的几种写法
在Kotlin的类中没有静态成员,也就是说你不能创建静态方法或者变量。那么我们该怎么写单例模式呢?
a.懒人写法
object Lazy {
}
相比java代码
public class Lazy {
private static Lazy instance = new Lazy();
private Lazy() {}
public static Lazy getInstance() {return instance;}
}
但是这种写法会在类加载时候就新建实例,拖慢应用启动速度。
b.懒加载
class LazyNotThreadSafe {
companion object {
//写法一:Kotlin上原生写法
//lazy关键字表示只有在这个instance被调用的时候才会调这个构造方法
//LazyThreadSafetyMode.NONE 不是线程安全
val instance by lazy(LazyThreadSafetyMode.NONE) {
LazyNotThreadSafe()
}
//写法二:Java转译
private var instance1: LazyNotThreadSafe? = null
fun newInstance(): LazyNotThreadSafe {
if (instance1 == null) instance1 = LazyNotThreadSafe()
return instance1!!
}
}
}
相比java代码
public class Lazy {
private static Lazy instance;
private Lazy() {}
public static Lazy getInstance() {
if (instance == null)
instance = new Lazy();
return instance;
}
}
c.加锁
class LazySychronized private constructor() {
companion object {
private var instance: LazySychronized? = null
@Synchronized
fun newInstance(): LazySychronized {
if (instance == null) instance = LazySychronized()
return instance!!
}
}
}
d.Double Check
class DoubleCheck private constructor() {
companion object {
val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
DoubleCheck()
}
}
}
public class SingleDoubleCheck {
//volatile保证赋值和构造是有序的
private static volatile SingleDoubleCheck instance;
private SingleDoubleCheck() {}
public static SingleDoubleCheck getInstance() {
if (instance == null) {
synchronized (SingleDoubleCheck.class) {
if (instance == null) {
//保证实例化和赋值顺序
instance = new SingleDoubleCheck();
}
}
}
return instance;
}
}
e.静态内部类
class InnerClass private constructor() {
private object Holder {
val instance = InnerClass()
}
companion object {
fun newInstance() = Holder.instance
}
}
public class InnerClass {
private static class Holder {
private static InnerClass instance = new InnerClass();
}
private InnerClass() {}
public static InnerClass getInstance() {return Holder.instance;}
}