Kotlin 学习之类和继承

类和继承

在 Kotlin 中类用 关键字class 声明:

class Invoice {
}

类的声明包含类名,类头(指定类型参数,主构造函数等等),以及类主体,用大括号包裹。类头和类体是可选的;如果没有类体可以省略大括号。

class Empty

构造函数

在 Kotlin 中类可以有一个主构造函数以及多个二级构造函数。主构造函数是类头的一部分:跟在类名后面(可以有可选的类型参数)。

class Person constructor(firstName: String) {
}

如果主构造函数没有注解或可见性说明,则 constructor 关键字是可以省略:

class Person(firstName: String) {
}

主构造函数不能包含任何代码,初始化代码可以放在以 init 做前缀的初始化块内。

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) { ... }

参看 可见性

二级构造函数

类也可以有二级构造函数,需要加前缀 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 = "")

创建类的实例

我们可以像使用普通函数那样使用构造函数创建类实例:

val invoice = Invoice()

val customer = Customer("Joe Smith")

注意 Kotlin 没有 new 关键字。
创建嵌套类、内部类或匿名类的实例参见嵌套类

Class Members

类可以包含:

继承

Kotlin 中所有的类都有共同的父类 Any ,它是一个没有父类声明的类的默认父类:

class Example // 隐式继承于 Any

Any 不是 java.lang.Object; 尤其是除了 equals(), hashCode() and toString()没有任何其他成员了。
参看 Java interoperability 了解更多信息。

声明一个明确的父类,需要在类头后加冒号再加父类:

open class Base(p: Int)

class Derived(p: Int) : Base(p)

如果类有主构造函数,则基类可以而且是必须在主构造函数中使用参数立即初始化。

I如果类没有主构造函数,则必须在每一个构造函数中用 super 关键字初始化基类,或者在代理另一个构造函数做这件事。注意在这种情形中不同的二级构造函数可以调用基类不同的构造方法:

class MyView : View {
    constructor(ctx: Context) : super(ctx)

    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}

open 注解与java中的 final 相反:它允许别的类继承这个类。默认情形下,kotlin中所有的类都是 final ,对应Effective Java,Item 17: Design and document for inheritance or else prohibit it.

复写方法

像之前提到的,我们在 kotlin 中坚持做明确的事。不像 java ,kotlin 需要把可以复写的成员(我们称之为open)都明确注解出来,并且重写它们:

open class Base {
    open fun v() {}
    fun nv() {}
}
class Derived() : Base() {
    override fun v() {}
}

对于 Derived.v() 来说 override 注解是必须的。如果没有加的话,编译器会报错。如果没有 open 注解,像 Base.nv() ,不管有没有添加override注解,在子类中声明一个同样签名的函数都是不合法的。在 final 类(就是没有open注解的类)中, open 类型的成员是不允许的。

标记为 override 的成员是open的,它可以在子类中被复写。如果你不想被重写就要加 final:

open class AnotherDerived() : Base() {
    final override fun v() {}
}

复写属性

复写属性与复写方法类似,在一个父类上声明的属性在子类上被重新声明,必须添加 override ,并且它们必须具有兼容的类型。每个被声明的属性都可以被一个带有初始化器的属性或带有getter方法的属性覆盖

open class Foo {
    open val x: Int get() { ... }
}

class Bar1 : Foo() {
    override val x: Int = ...
}

您还可以使用 var 属性覆盖一个 val 属性,但反之则不允许。这是允许的,因为 val 属性本质上声明了一个getter方法,并将其重写为 var ,另外在派生类中声明了setter方法。

注意,可以在主构造函数中使用 override 关键字作为属性声明的一部分。

interface Foo {
    val count: Int
}

class Bar1(override val count: Int) : Foo

class Bar2 : Foo {
    override var count: Int = 0
}

调用父类的实现

在派生类中的代码可以使用super关键字调用基类的函数和属性访问器的实现:

open class Foo {
    open fun f() { println("Foo.f()") }
    open val x: Int get() = 1
}

class Bar : Foo() {
    override fun f() { 
        super.f()
        println("Bar.f()") 
    }

    override val x: Int get() = super.x + 1
}

在内部类中,访问外部类的父类是通过super关键字配合外部类的类名super@Outer

class Bar : Foo() {
    override fun f() { /* ... */ }
    override val x: Int get() = 0

    inner class Baz {
        fun g() {
            super@Bar.f() // Calls Foo's implementation of f()
            println(super@Bar.x) // Uses Foo's implementation of x's getter
        }
    }
}

复写规则

在 kotlin 中,实现继承通常遵循如下规则:如果一个类从它的直接父类继承了同一个成员的多个实现,那么它必须复写这个成员并且提供自己的实现(或许只是直接用了继承来的实现)。为表示使用父类中提供的方法我们用 super<Base> 表示:

open class A {
    open fun f() { print("A") }
    fun a() { print("a") }
}

interface B {
    fun f() { print("B") } // interface members are 'open' by default
    fun b() { print("b") }
}

class C() : A(), B {
    // The compiler requires f() to be overridden:
    override fun f() {
        super<A>.f() // call to A.f()
        super<B>.f() // call to B.f()
    }
}

可以同时从 A 和 B 中继承方法,而且 C 继承 a()b() 的实现没有任何问题,因为它们都只有一个实现。但是 f() 有俩个实现,因此我们在 C 中必须复写 f() 并且提供 自己的实现来消除歧义。

抽象类

一个类或一些成员可能被声明成 abstract ,一个抽象方法在它的类中没有实现方法,记住我们不用给一个抽象类或函数添加 open 注解,它默认是带着的。

我们可以用一个抽象成员去复写一个带 open 注解的非抽象方法。

open class Base {
    open fun f() {}
}

abstract class Derived : Base() {
    override abstract fun f()
}

伴随对象

在 kotlin 中不像 java 或者 C# 它没有静态方法。在大多数情形下,我们建议只用包级别的函数。

如果你要写一个没有实例类就可以调用的方法,但需要访问到类内部(比如说一个工厂方法),你可以把它写成它所在类的一个 对象的声明

更高效的方法是,你可以在你的类中声明一个伴随对象 ,这样你就可以像 java/c#那样把它当做静态方法调用,只需要它的类名做一个识别就好了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin是一种现代化的静态类型编程语言,它可以在Java虚拟机(JVM)上运行,并且与Java语言高度兼容。如果你想学习Kotlin,以下是一个学习路线的建议: 1. 了解基础知识:开始学习Kotlin之前,建议先了解一些基本的编程概念和语法,比如变量、数据类型、条件语句、循环语句等。这将为你后续的学习打下坚实的基础。 2. 学习Kotlin语法:Kotlin与Java有很多相似之处,但也有一些不同之处。学习Kotlin的语法和特性,比如空安全、扩展函数、数据类等,可以通过官方文档、教程或在线课程来学习。 3. 实践编程:通过编写一些简单的程序来巩固所学的知识。可以尝试解决一些小问题或者完成一些小项目,这样可以更好地理解和应用Kotlin的语法和特性。 4. 深入学习面向对象编程:Kotlin是一种面向对象的编程语言,深入学习面向对象编程的概念和原则对于掌握Kotlin非常重要。学习类、对象、继承、多态等概念,并尝试在Kotlin中应用这些概念。 5. 学习Kotlin与Java的互操作性:由于Kotlin与Java高度兼容,学习如何在Kotlin中使用Java类库和框架是非常重要的。了解如何调用Java代码、处理Java集合等,可以帮助你更好地使用Kotlin进行开发。 6. 深入学习Kotlin的高级特性:一旦掌握了Kotlin的基础知识,可以进一步学习一些高级特性,比如协程、函数式编程、DSL等。这些特性可以提高你的代码质量和开发效率。 7. 参与实际项目:最好的学习方式是通过实践。尝试参与一些实际的项目或者开源项目,与其他开发者合作,这样可以提升你的编程能力和实践经验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值