Kotlin基础语法(二)

Posted by alonealice on 2017-06-06

类和对象

在Kotlin中,类依旧使用class定义,用大括号包裹,不过当类里面没有任何代码时,大括号可以省略。

构造函数

Kotlin中可以有一个主构造函数和多个二级构造函数,如下:

1
2
class A  constructor(name: String){
}

其中的constructor关键字可以省略。但是如果构造函数有其他声明获取注解,如:private,那么constructor就不能省略。
在主构造函数中不能有任何代码,所以初始化代码需要写在init代码块中,或者在参数声明时定义:

1
2
3
4
5
6
7
8
9
class A  (name: String){
init {
print(name)
}
}

class A (name: String){
var userName=name
}

属性声明可以主构造函数中直接声明:

1
2
class A  (var userName: String){
}

二级构造函数声明在类体内,但是必须有constructor关键字,如下:

1
2
3
4
5
class A {
constructor(name: String){

}
}

如果类已经有了一级构造函数,那么二级构造函数在声明时必须代理主构造函数,并且使用关键字this

1
2
3
4
5
6
7
class A (var name: String){

constructor(name: String,age: Int) : this(name){

}

}

两个构造函数在使用时没有区别,所以在声明时参数数量或类型需要不同。
在创建类的实例时,不需要关键字new,可以直接创建。

1
val a=A("tim")

属性和字段

类中的属性用var声明时可变的,用val声明是只读的。使用属性时只要直接用名称使用它即可,如:a.userName
声明属性的示例:

1
2
var userName: String?=null
var age=1

在确定了属性的类型后,必须要指定其默认值。

get和set方法

除了在声明属性时指定默认值以外,还可以在get方法中设置值。Kotlin中get和set方法都是默认的,但是我们也可以自定义get和set方法。

1
2
var userName: String?
get() = "tom"

这里userName开始没有默认值,但是在get方法中设置了值。

1
2
3
4
var userName: String?=null
set(value) {
userName=value+"."
}

这里userName传入的值后面添加一个.
如果你需要改变一个访问器的可见性或者对其注解,但是不需要改变默认的实现, 你可以定义访问器而不定义其实现:

1
2
var userName: String?=null
private set(value) {}

数据类

我们经常创建一些只保存数据的类。在这些类中,一些标准函数往往是从 数据机械推导而来的。在 Kotlin 中,这叫做数据类并标记为data

1
data class User(val name: String, val age: Int)

编译器自动从主构造函数中声明的所有属性导出以下成员:equals()/hashCode() 对,toString() 格式是 “User(name=John, age=42)”,componentN() 函数 按声明顺序对应于所有属性,copy()函数。
对于上面的数据类,程序会自动生成copy函数:

1
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)

copy方法使用如下:

1
2
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)

密封类

密封类用来表示受限的类继承结构:当一个值为有限集中的类型、而不能有任何其他类型时(所有的子类都必须在密封类的内部,继承子类的类可以在其他文件)。

1
2
3
4
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

使用密封类的关键好处在于使用 when 表达式 的时候,如果能够验证语句覆盖了所有情况,就不需要为该语句再添加一个 else 子句了。

嵌套类

类可以嵌套在其他类中

1
2
3
4
5
6
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
内部类

类可以标记为inner以便能够访问外部类的成员,这个类就是内部类。内部类会带有一个对外部类的对象的引用:

1
2
3
4
5
6
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = bar
}
}
匿名内部类

对象表达式创建匿名内部类:

1
2
3
4
5
6
window.addMouseListener(object: MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
}
override fun mouseEntered(e: MouseEvent) {
}
})

函数

Kotlin 中的函数使用 fun 关键字声明

1
2
3
fun setName(name: String) : Int{
return 1
}

后面的: Int表示返回值为Int,如果不返回任何值,则使用Unit,如:

1
2
3
fun setName(name: String) : Unit{
this.userName=name
}

函数在声明时可以直接设置默认参数,如:

1
2
3
fun setName(name: String="tim") : Unit{
this.userName=name
}

覆盖方法总是使用与基类型方法相同的默认参数值。 当覆盖一个带有默认参数值的方法时,必须从签名中省略默认参数值。
可以在调用函数时使用命名的函数参数。当一个函数有大量的参数或默认参数时这会非常方便。如:

1
2
3
fun setName(name: String="tim",a: Boolean=false,b: Boolean=false,c: String) : Unit{
this.userName=name
}

使用命名参数,增加可读性,同时也可以省略部分参数,如:

1
a.setName("ddd",c="d")

当函数返回单个表达式时,可以省略花括号并且在 = 符号之后指定代码体即可,如:

1
fun double(x: Int): Int = x * 2

函数的参数还可以使用Varargs来表示可变数量的参数,如:

1
2
3
4
5
6
7
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts is an Array
result.add(t)
return result
}
val list = asList(1, 2, 3)

在 Kotlin 中函数可以在文件顶层声明,所有你不需要用类来保存一个函数。此外 除了顶层函数,Kotlin 中函数也可以声明在局部作用域、作为成员函数以及扩展函数。而顶层函数在调用时不需要实例就可以直接调用。
Kotlin 支持局部函数,即一个函数在另一个函数内部。如:

1
2
3
4
5
6
7
8
9
fun dfs(graph: Graph) {
fun dfs(current: Vertex, visited: Set<Vertex>) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v, visited)
}

dfs(graph.vertices[0], HashSet())
}

参考资料