Kotlin最简单的入门教程——类和继承
2017-06-15 10:41
369 查看
Kotlin 中类和java中的类声明都是用关键字class
两者的区别主要在于构造函数:
java中没有主构造函数次构造函数之分,但是kotlin有
如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字。
主构造函数不能包含任何的代码。初始化的代码可以放 到以 init 关键字作为前缀的初始化块(initializer blocks)中:
注意,主构造的参数可以在初始化块中使用。它们也可以在 类体内声明的属性初始化器中使用:
事实上,声明属性以及从主构造函数初始化属性,Kotlin 有简洁的语法:
与普通属性一样,主构造函数中声明的属性可以是 可变的(var)或只读的(val)。
如果构造函数有注解或可见性修饰符,这个 constructor 关键字是必需的,并且 这些修饰符在它前面:
如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数 用 this 关键字即可:
如果一个非抽象类没有声明任何(主或次)构造函数,它会有一个生成的 不带参数的主构造函数。构造函数的可见性是 public。如果你不希望你的类 有一个公有构造函数,你需要声明一个带有非默认可见性的空的主构造函数:
注意:在 JVM 上,如果主构造函数的所有的参数都有默认值,编译器会生成 一个额外的无参构造函数,它将使用默认值。这使得 Kotlin 更易于使用像 Jackson 或者 JPA 这样的通过无参构造函数创建类的实例的库。
注意 Kotlin 并没有 new 关键字。
Any不是 java.lang.Object;尤其是,它除了 equals()、hashCode()和toString()外没有任何成员。
要声明一个显式的超类型,我们把类型放到类头的冒号之后:
如果该类有一个主构造函数,其基类型可以(并且必须) 用(基类型的)主构造函数参数就地初始化。
如果类没有主构造函数,那么每个次构造函数必须 使用 super 关键字初始化其基类型,或委托给另一个构造函数做到这一点。 注意,在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数:
类上的 open 标注与 Java 中 final 相反,它允许其他类 从这个类继承。默认情况下,在 Kotlin 中所有的类都是 final,
不写open关键字的
类
方法,
属性
是不能被继承和重写的
(1)要在父类被重写的方法或属性前加open关键字,并且子类中重写的方法或属性前都要加override关键字
(2)可以用一个 var 属性覆盖一个 val 属性,但反之则不行。这是允许的,因为一个 val 属性本质上声明了一个 getter 方法,而将其覆盖为 var 只是在子类中额外声明一个 setter 方法
(3)覆盖方法总是使用与基类型方法相同的默认参数值。 当覆盖一个带有默认参数值的方法时,必须把子类重写的中省略默认参数值
例子
(2)方法可以嵌套
局部函数可以访问外部函数(即闭包)的局部变量,所以在上例中,visited 可以是局部变量。
(2)类可以标记为 inner 以便能够访问外部类的成员。内部类会带有一个对外部类的对象的引用:
在接口中声明的属性要么是抽象的,要么提供 访问器的实现。
(2)接口成员默认就是“open”的
(3)子类继承父类和实现接口的写法 父类需要添加(),而接口不需要
两者的区别主要在于构造函数:
java中没有主构造函数次构造函数之分,但是kotlin有
1 主构造函数
在 Kotlin 中的一个类可以有一个主构造函数和一个或多个次构造函数。主 构造函数是类头的一部分:它跟在类名(和可选的类型参数)后。class Person constructor(firstName: String) { }
如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字。
class Person(firstName: String) { }
主构造函数不能包含任何的代码。初始化的代码可以放 到以 init 关键字作为前缀的初始化块(initializer blocks)中:
class Customer(name: String) { init { logger.info("Customer initialized with value ${name}") } }
注意,主构造的参数可以在初始化块中使用。它们也可以在 类体内声明的属性初始化器中使用:
class Customer(name: String) { val customerKey = name.toUpperCase() }
事实上,声明属性以及从主构造函数初始化属性,Kotlin 有简洁的语法:
class Person(val firstName: String, val lastName: String, var age: Int) { // …… }
与普通属性一样,主构造函数中声明的属性可以是 可变的(var)或只读的(val)。
如果构造函数有注解或可见性修饰符,这个 constructor 关键字是必需的,并且 这些修饰符在它前面:
class Customer public @Inject constructor(name: String) { …… }
2 次构造函数
类也可以声明前缀有 constructor的次构造函数:class Person { constructor(parent: Person) { parent.children.add(this) } }
如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数 用 this 关键字即可:
class Person(val name: String) { constructor(name: String, parent: Person) : this(name) { parent.children.add(this) } }
如果一个非抽象类没有声明任何(主或次)构造函数,它会有一个生成的 不带参数的主构造函数。构造函数的可见性是 public。如果你不希望你的类 有一个公有构造函数,你需要声明一个带有非默认可见性的空的主构造函数:
class DontCreateMe private constructor () { }
注意:在 JVM 上,如果主构造函数的所有的参数都有默认值,编译器会生成 一个额外的无参构造函数,它将使用默认值。这使得 Kotlin 更易于使用像 Jackson 或者 JPA 这样的通过无参构造函数创建类的实例的库。
class Customer(val customerName: String = "")
3 创建类的实例
要创建一个类的实例,我们就像普通函数一样调用构造函数:val invoice = Invoice() val customer = Customer("Joe Smith")
注意 Kotlin 并没有 new 关键字。
4 继承
在 Kotlin 中所有类都有一个共同的超类 Any,这对于没有超类型声明的类是默认超类:class Example // 从 Any 隐式继承
Any不是 java.lang.Object;尤其是,它除了 equals()、hashCode()和toString()外没有任何成员。
要声明一个显式的超类型,我们把类型放到类头的冒号之后:
open class Base(p: Int)class Derived(p: Int) : Base(p)
如果该类有一个主构造函数,其基类型可以(并且必须) 用(基类型的)主构造函数参数就地初始化。
如果类没有主构造函数,那么每个次构造函数必须 使用 super 关键字初始化其基类型,或委托给另一个构造函数做到这一点。 注意,在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数:
class MyView : View { constructor(ctx: Context) : super(ctx) constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)}
类上的 open 标注与 Java 中 final 相反,它允许其他类 从这个类继承。默认情况下,在 Kotlin 中所有的类都是 final,
5 方法重写
Derived.v() 函数上必须加上 override标注。如果没写,编译器将会报错。 如果函数没有标注 open 如 Base.nv(),则子类中不允许定义相同签名的函数, 不论加不加 override。在一个 final 类中(没有用 open 标注的类),开放成员是禁止的。不写open关键字的
类
方法,
属性
是不能被继承和重写的
(1)要在父类被重写的方法或属性前加open关键字,并且子类中重写的方法或属性前都要加override关键字
(2)可以用一个 var 属性覆盖一个 val 属性,但反之则不行。这是允许的,因为一个 val 属性本质上声明了一个 getter 方法,而将其覆盖为 var 只是在子类中额外声明一个 setter 方法
(3)覆盖方法总是使用与基类型方法相同的默认参数值。 当覆盖一个带有默认参数值的方法时,必须把子类重写的中省略默认参数值
例子
open class Base { var a:String = "" open val b:String = "" open fun v() {} open fun canshu(i: Int = 10) {} fun nv() {} } class Derived() : Base() { override var a:String = ""//错误的写法,要在父类被重写的方法或属性前加open关键字,并且子类中重写的方法或属性前都要加override关键字 override var b:String = ""//可以用一个 var 属性覆盖一个 val 属性,但反之则不行。这是允许的,因为一个 val 属性本质上声明了一个 getter 方法,而将其覆盖为 var 只是在子类中额外声明一个 setter 方法 override fun v() {} open fun canshu(i: Int = 10) {}//错误的写法,可以有参数但是不能有默认值 fun nv(){} //错误的写法 }
6 返回 Unit 的函数
如果一个函数不返回任何有用的值,它的返回类型是 Unit。Unit 是一种只有一个值——Unit 的类型。这个 值可写可不写fun printHello(name: String?): Unit { if (name != null) println("Hello ${name}") else println("Hi there!") // `return Unit` 或者 `return` 是可选的 } fun printHello(name: String?) { //和上面的方法是一模一样的效果 if (name != null) println("Hello ${name}") else println("Hi there!") // `return Unit` 或者 `return` 是可选的 }
7 单表达式函数
与java的不同的是,kotlin的一个方法可以这样写:fun double(x: Int): Int = x * 2
8 方法
(1)如果方法的参数是可变数量的参数 则需要添加关键字(Varargs)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 }
(2)方法可以嵌套
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()) }
局部函数可以访问外部函数(即闭包)的局部变量,所以在上例中,visited 可以是局部变量。
fun dfs(graph: String) { val visited = HashSet<String>() fun dfs(current: String) { visited.add("1") } }
9 内部类
(1)匿名内部类:关键字object(2)类可以标记为 inner 以便能够访问外部类的成员。内部类会带有一个对外部类的对象的引用:
class Outer { private val bar: Int = 1 inner class Inner { fun foo() = bar } //匿名内部类:关键字object window.addMouseListener(object: MouseAdapter() { override fun mouseClicked(e: MouseEvent) { // …… } override fun mouseEntered(e: MouseEvent) { // …… } }) } //类可以标记为 inner 以便能够访问外部类的成员。内部类会带有一个对外部类的对象的引用 val demo = Outer().Inner().foo() // == 1
10 接口
既包含抽象方法的声明,也包含 实现interface MyInterface { fun bar() fun foo() { // 可选的方法体 } } //实现接口:一个类或者对象可以实现一个或多个接口。 class Child : MyInterface { override fun bar() { // 方法体 } }
在接口中声明的属性要么是抽象的,要么提供 访问器的实现。
interface MyInterface { // 抽象的 val prop: Int //访问器 val propertyWithImplementation: String get() = "test" fun foo() { print(prop) } }
11 子类继承父类实现接口
(1)为了表示采用从哪个超类型继承的实现,我们使用由尖括号中超类型名限定的 super(2)接口成员默认就是“open”的
(3)子类继承父类和实现接口的写法 父类需要添加(),而接口不需要
open class A { open fun f() { print("A") } fun a() { print("a") } } interface B { fun f() { print("B") } // 接口成员默认就是“open”的 fun b() { print("b") } fun c() } class C: A(), B { override fun c() { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } // 编译器要求覆盖 f(): override fun f() { super<A>.f() // 调用 A.f() super<B>.f() // 调用 B.f() } }
最后欢迎加入Kotlin QQ群,一起讨论学习:
欢迎关注 微信公号
相关文章推荐
- Kotlin最简单的入门教程——可见性修饰符,扩展,数据类,密封类
- Kotlin最简单的入门教程——与Java互相调用
- Kotlin最简单的入门教程——委托
- Kotlin最简单的入门教程——集合
- Kotlin最简单的入门教程——基础类型(一)
- Kotlin最简单的入门教程——关键字,操作符,异常,空指针
- Kotlin最简单的入门教程——神秘的Lambda
- Kotlin最简单的入门教程——环境
- Kotlin最简单的入门教程——基础类型(二)
- Ajax PHP简单入门教程代码
- Ajax+PHP简单基础入门实例教程
- Ajax+PHP简单基础入门实例教程
- 网页游戏开发入门教程三(简单程序应用)
- Ajax+PHP简单入门教程
- Ajax+PHP简单入门教程
- Python3 入门教程 简单但比较不错
- php简单入门视频教程
- 网页游戏开发入门教程三(简单程序应用)
- php简单入门视频教程
- Ajax+PHP简单入门教程