Kotlin从入门到放弃之基础篇(六)
泛型
像Java中一样Kotlin中也存在泛型,用来解耦类与函数所用类型之间的约束,而且使用方法是一致的。
泛型类
//创建一个泛型
class Box<T>(t : T){
var value = t
}
//使用
val box : Box<Int> = Box<Int>(1)
♣ 如果类型参数可以通过推断得到,比如来自构造函数的参数等。那么类型参数可以忽略:
//通过1推断出Int类型
val box : Box = Box(1)
泛型参数
fun <T> doSomething(type : T){
when(type){
is Int ->println("this is int")
is String ->println("this is string")
else -> println("this is else")
}
}
♣ 类型参数放在函数名称之前。
♣ 当调用时明确的传入了参数类型,那么类型参数应放在函数名称之后。如果不传入参数类型,编译器则根据传入的值自动判断参数类型。
//使用泛型方法
doSomething(1)//不带参数类型 输出‘this is int’
doSomething("123")//携带参数类型 输出‘this is string’
(有关泛型的更多知识,后续章节进行详细讨论)
嵌套类
将一个类定义在另一个类的内部,叫做嵌套类
class Outer{
class Demo{
fun test(){
println("这是内部类")
}
}
}
//使用内部嵌套类的成员
Outer.Demo().test()//输出“这是内部类”
♣ 嵌套类的的实例创建的时候需要先创建外部类的实例,以此为基础进行创建。
内部类
内部定义的类可以通过inner关键字进行标记,然后它就可以访问外部类的成员了
class Outer{
var a : Int = 1
inner class Demo{
var b=a
}
}
匿名内部类
匿名内部类的实例通过对象表达式创建:
window.addMouseListener(object: MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ……
}
override fun mouseEntered(e: MouseEvent) {
// ……
}
})
如果对象是函数式 Java 接口(即具有单个抽象方法的 Java 接口)的实例, 你可以使用带接口类型前缀的lambda表达式创建它:
val listener = ActionListener { println("clicked") }
枚举类
枚举类最基本的用法是实现一个类型安全的枚举
enum class Direction{
NORTH,SOUTH,WEST,EAST
}
♣ 每个自枚举常量都是一个对象,用“,”分隔开
初始化
每一个枚举都是枚举类的实例,它可以被初始化
enum class Size(val size : Int){
S(150),
M(160),
L(170),
}
匿名类
枚举常量也可以定义在它们自己的匿名类里
enum class ProtocolState {
WAITING {
override fun signal() = TALKING
},
TALKING {
override fun signal() = WAITING
}; //使用分号将枚举常量定义和成员定义分开
abstract fun signal(): ProtocolState
}
可以有对应的方法,以及复写基本方法。注意,如果枚举定义了任何成员,你需要像在Java中那样用分号;把枚举常量定义和成员定义分开。
使用枚举常量
Kotlin中的枚举类有合成方法允许列出枚举常量的定义并且通过名字获得枚举常量。
fun main(args: Array<String>){
print(Direction.valueOf("EAST"))//输出EAST
print(Direction.EAST)//输出EAST
print(Direction.values())//输出Array<Direction>
}
enum class Direction(val direction : String){
NORTH("north"),
WEST("west"),
SOUTH("south"),
EAST("east")
}
每个枚举常量都有获取在枚举类中的名称和位置:
Direction.EAST.name//结果为EAST
Direction.EAST.ordinal//结果为3(枚举常量的位置从0开始)
对象表达式和声明
有时候我们需要创建一个对当前类有修改的对象,但是不想重新声明一个子类。java用匿名内部类解决了这一问题,在Kotlin中用对象表达式来实现。
对象表达式
创建继承自某匿名类的对象
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
}
override fun mouseEntered(e: MouseEvent) {
// ...
}
})
如果父类有构造函数,则必须给构造函数赋值,多个父类可以用逗号分隔开
open class A(x : Int){
public open val y : Int = x
}
interface B{...}
val ab = object:A(1),B{
override val y = 2
}
有时候我们只需要一个没有父类的对象,可以这样写:
val icbc = object{
val x : Int = 1
val y : Int = 2
}
print{"x: $icbc.x y: $icbc.y"}
和Java相同的是对象表达式可以访问闭合范围内的变量,但是Kotlin中访问的变量可以不用final修饰
fun countClicks(window: JComponent) {
var clickCount = 0//闭合范围内的变量不需要final修饰即能被使用
var enterCount = 0
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
clickCount++
}
override fun mouseEntered(e: MouseEvent) {
enterCount++
}
})
}
对象声明
在object关键字后面指定要声明的类的名称,则该声明不再是‘对象表达式’,而是‘对象声明’。此时,该对象不再是表达式,不能作为赋值语句使用。如果要访问这个类,可以通过名称直接访问。
open class Person(name : String){
open val name :String = name
//...
}
object myDemo :Person("小明"),InspectorClient{
override fun sendMessageToFrontend(message: String?): Boolean {
TODO("not implemented")
//...
}
override val name :String = "测试"
}
♣ 对象声明不可以是局部的(不可以嵌套在函数内),但可以嵌套在另一个对象生命内活着嵌套在另一个非内部类中。
♣ 被声明的对象可以存在父类(类和接口)
♣ 对象声明不能指定构造函数
♣ 如果对象声明的父类存在构造函数,则必须在构造器中传递相应参数
伴随对象
Kotlin中不支持静态的方法,Kotlin提供了一个companion关键字用来标记对象声明。是为伴随对象
class Person (var name : String ,var age : Int){
companion object Student{
val score : Int = 60
fun printScore(score : Int){
print(score)
}
}
}
fun main(args: Array<String>){
print(Person.Student.score)
print(Person.Student.printScore(60))
print(Person.printScore(60))
//上述均输出60
}
从上述代码可以看出,伴随对象的调用有两种方式:类名.伴随对象名.成员 或者 类名.伴随对象名.成员
对象声明、对象表达式、伴随对象的区别:
♣ 对象表达式在声明的时候被立即执行
♣ 对象声明却是懒加载的,只有在被第一次访问的时候才会执行。
♣ 伴随对象所在的类被加载时伴随对象则被初始化,与Java静态成员一样。