Kotlin-2.1-类和继承
来源:互联网 发布:java 第三方网络请求 编辑:程序博客网 时间:2024/05/29 12:10
Kotlin-2.1-Classes and Inheritance
标签:Kotlin
翻译自官方文档:Classes and Inheritance-如有遗漏和错误,欢迎指正!
本节介绍了Kotlin中类和继承的内容。类中包含首要构造器和次要构造器,还有类成员:属性(Java中成员变量)和函数(Java中方法)。继承主要讲解的是如何重载父类的方法和属性以及如何调用父类的实现。最后归纳总结重载的原则。
- Kotlin-21-Classes and Inheritance
- 1-类和继承
- 1-类
- 1-构造器构造函数
- 2-次要构造器
- 3-创建类的实例
- 4-类成员
- 2-继承
- 1-重载方法
- 2-Overriding Properties
- 3-Calling the superclass implementation
- 4-Overriding Rules
- 3-Abstract Classes
- 4-Companion Objects
- 1-类
- 2-Classes and Inheritance原文
- 1-Classes
- 1-Constructors
- 2-Secondary Constructors
- 3-Creating instances of classes
- 4-Class Members
- 2-Inheritance
- 1-Overriding Methods
- 2-Overriding Properties
- 3-Calling the superclass implementation
- 4-Overriding Rules
- 3-Abstract Classes
- 4-Companion Objects
- 1-Classes
1-类和继承
1-类
在Kotlin中类使用关键字class
声明:
Classes in Kotlin are declared using the keyword class
:
class Invoice {}
类声明由类名,类头和类主体三部分组成,用大括号包裹。其中类头(class header)指明了类的类型参数和首要构造器等内容。类头和类主体都是可选的,如果类没有主体,大括号可以被省略:
class Empty
1-构造器/构造函数
在Kotlin中的类可以有一个首要构造函数(primary constructor)和一个或者多个次要构造函数(secondary constructors)。首要构造函数是类头的一部分:它跟在类名的后面并拥有可选的类型参数,如下:
class Person constructor(firstName: String) {}
如果首要构造器没有任何注解
或者可见性修饰符
,该constructor
关键字可以被省略,如下:
class Person(firstName: String) {}
可见性修饰符:类似于java中的public,private等修饰符。
首要构造器
不包含任何代码。初始化代码
被放置在initializer blocks
初始化代码块中, 该代码块以关键字init
作为前缀。
在一个实例初始化的期间,初始化代码块
以它们在类主体中出现的相同顺序,顺序执行
。它们可以和属性初始化
纵横交错:
class InitOrderDemo(name: String) {//属性初始化 val firstProperty = "First property: $name".also(::println)//第一段初始化代码块 init { println("First initializer block that prints ${name}") }//属性初始化 val secondProperty = "Second property: ${name.length}".also(::println)//第二段初始化代码块 init { println("Second initializer block that prints ${name.length}") }}fun main(args: Array<String>) { InitOrderDemo("hello")}
注意:首要构造器的参数可以被初始化代码块所使用。它们也可以被在类主体中声明的属性初始化所使用:
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) { ... }
更多细节,参考Visibility Modifiers
(可见性修饰符)。
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) }}
注意:在初始化代码块的代码高效地成为首要构造器的一部分。首要构造器的代理会作为次要构造器的第一行语句执行,所以在所有初始化代码块的代码都在次要构造器之前执行。甚至,如果类没有首要构造器,代理都会隐式地发生,初始化代码块依旧会执行:
class Constructors { init { println("Init block") } constructor(i: Int) { //次要构造器,在没有首要构造器时,依然先执行了init代码块内容。 println("Constructor") }}fun main(args: Array<String>) { Constructors(1)}
执行结果:
Init block
Constructor
如果,一个非抽象类没有声明任何构造器(包括首要和次要),类仍然会生成一个没有参数的首要构造器。该构造器的可见性是public
、如果你不想你的类有一个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
。
嵌套类、内部类、匿名内部类的创建在Nested classes
嵌套类中进行介绍。
4-类成员
类可以包含:
- 构造器和初始化代码块
- 函数
- 属性
- 嵌套和内部类
- 对象声明
2-继承
在Kotlin中所有类都有一个共同的超类Any
, 该超类是默认的,不需要超类的声明:
class Example // 隐式继承自Any
Any
不是java.lang.Object
;特别地,它除了equals(), hashCode() and toString()
没有其他任何成员。请参考Java interoperability(Java互通性)章节。
为了声明一个显式的超类,我们将类型放置在类头后的冒号后面:
open class Base(p: Int)class Derived(p: Int) : Base(p)
如果类(子类
)有首要构造器,这个基本类型(超类
)必须在这里被初始化,通过使用首要构造器的参数。
如果类(子类
)没有首要构造器,每个次要构造器必须使用关键字super初始化基本类型(超类
),或者代理给另一个完成该任务的构造器。注意,在这种情况下,不同的次要构造器可以调用基本类型(超类
)的不同的构造器:
//没有首要构造器,只有次要构造器class MyView : View { constructor(ctx: Context) : super(ctx) //MyView的次要构造器初始化父类,通过使用View的super(ctx)构造器。 constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) //MyView使用了View的另一种构造器}
open
注解在类中是与java
中的final
相反的内容:open
允许其他类继承自该类。默认的,在Kotlin中所有的类都是final
,这对应于Effective Java
, Item 17: Design and document for inheritance or else prohibit it.(继承的设计和文档,否则禁止它.-不知道如何翻译,很不通顺。)
补充:本人亲测,在Kotlin中所有类默认都是无法被继承的,因为默认是
final
的。只有用open
修饰的类才可以被继承。
1-重载方法
正如我们之前提到的,我们坚持在Kotlin中让事情都是显式的。不像Java,Kotlin对于覆盖的成员(这些成员需要有open
修饰符)需要显式的注释:
open class Base { open fun v() {} //open修饰 fun nv() {}}class Derived() : Base() { override fun v() {} //显式覆盖}
override
注解需要修饰 Derived.v()
. 如果该注解遗漏,编译器就会报错。如果一个函数没有open
注解,类似Base.nv(), 在子类中声明相同签名的方法就是非法的,无论是否有overide
。在一个final
类中(即,一个没有open
注解的类), 禁止拥有open
的成员(open
修饰符无效).
被overide
标记的成员其本身就是open
的,即,该成员可以在子类被重载。如果你想禁止“再重载”(重载父类的方法继续被子类重载),可以使用final
关键字:
open class AnotherDerived() : Base() { final override fun v() {} //不会继续被子类重载。}
2-Overriding Properties
Overriding properties works in a similar way to overriding methods; properties declared on a superclass that are then redeclared on a derived class must be prefaced with override
, and they must have a compatible type. Each declared property can be overridden by a property with an initializer or by a property with a getter method.
open class Foo { open val x: Int get() { ... }}class Bar1 : Foo() { override val x: Int = ...}
You can also override a val
property with a var
property, but not vice versa. This is allowed because a val
property essentially declares a getter method, and overriding it as a var
additionally declares a setter method in the derived class.
Note that you can use the override
keyword as part of the property declaration in a primary constructor.
interface Foo { val count: Int}class Bar1(override val count: Int) : Fooclass Bar2 : Foo { override var count: Int = 0}
3-Calling the superclass implementation
Code in a derived class can call its superclass functions and property accessors implementations using the super
keyword:
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}
Inside an inner class, accessing the superclass of the outer class is done with the super keyword qualified with the outer class name: 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 } }}
4-Overriding Rules
In Kotlin, implementation inheritance is regulated by the following rule: if a class inherits many implementations of the same member from its immediate superclasses, it must override this member and provide its own implementation (perhaps, using one of the inherited ones). To denote the supertype from which the inherited implementation is taken, we use super
qualified by the supertype name in angle brackets, e.g. 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() }}
It’s fine to inherit from both A
and B
, and we have no problems with a()
and b()
since C
inherits only one implementation of each of these functions. But for f()
we have two implementations inherited by C
, and thus we have to override f()
in C
and provide our own implementation that eliminates the ambiguity.
3-Abstract Classes
A class and some of its members may be declared abstract
. An abstract member does not have an implementation in its class. Note that we do not need to annotate an abstract class or function with open – it goes without saying.
We can override a non-abstract open member with an abstract one
open class Base { open fun f() {}}abstract class Derived : Base() { override abstract fun f()}
4-Companion Objects
In Kotlin, unlike Java or C#, classes do not have static methods. In most cases, it’s recommended to simply use package-level functions instead.
If you need to write a function that can be called without having a class instance but needs access to the internals of a class (for example, a factory method), you can write it as a member of an object declaration
inside that class.
Even more specifically, if you declare a companion object
inside your class, you’ll be able to call its members with the same syntax as calling static methods in Java/C#, using only the class name as a qualifier.
**翻译自官方文档:Classes and Inheritance
下面是英文原版**
2-Classes and Inheritance(原文)
1-Classes
Classes in Kotlin are declared using the keyword class
:
class Invoice {}
The class declaration consists of the class name, the class header (specifying its type parameters, the primary constructor etc.) and the class body, surrounded by curly braces. Both the header and the body are optional; if the class has no body, curly braces can be omitted.
class Empty
1-Constructors
A class in Kotlin can have a primary constructor and one or more secondary constructors. The primary constructor is part of the class header: it goes after the class name (and optional type parameters).
class Person constructor(firstName: String) {}
If the primary constructor does not have any annotations or visibility modifiers, the constructor
keyword can be omitted:
class Person(firstName: String) {}
The primary constructor cannot contain any code. Initialization code can be placed in initializer blocks
, which are prefixed with the init
keyword.
During an instance initialization, the initializer blocks are executed in the same order as they appear in the class body, interleaved with the property initializers:
//sampleStartclass InitOrderDemo(name: String) { val firstProperty = "First property: $name".also(::println) init { println("First initializer block that prints ${name}") } val secondProperty = "Second property: ${name.length}".also(::println) init { println("Second initializer block that prints ${name.length}") }}//sampleEndfun main(args: Array<String>) { InitOrderDemo("hello")}
Note that parameters of the primary constructor can be used in the initializer blocks. They can also be used in property initializers declared in the class body:
class Customer(name: String) { val customerKey = name.toUpperCase()}
In fact, for declaring properties and initializing them from the primary constructor, Kotlin has a concise syntax:
class Person(val firstName: String, val lastName: String, var age: Int) { // ...}
Much the same way as regular properties, the properties declared in the primary constructor can be mutable (var
) or read-only (val
).
If the constructor has annotations or visibility modifiers, the constructor keyword is required, and the modifiers go before it:
class Customer public @Inject constructor(name: String) { ... }
For more details, see Visibility Modifiers
.
2-Secondary Constructors
The class can also declare secondary constructors, which are prefixed with constructor:
class Person { constructor(parent: Person) { parent.children.add(this) }}
If the class has a primary constructor, each secondary constructor needs to delegate to the primary constructor, either directly or indirectly through another secondary constructor(s). Delegation to another constructor of the same class is done using the this keyword:
class Person(val name: String) { constructor(name: String, parent: Person) : this(name) { parent.children.add(this) }}
Note that code in initializer blocks effectively becomes part of the primary constructor. Delegation to the primary constructor happens as the first statement of a secondary constructor, so the code in all initializer blocks is executed before the secondary constructor body. Even if the class has no primary constructor, the delegation still happens implicitly, and the initializer blocks are still executed:
//sampleStartclass Constructors { init { println("Init block") } constructor(i: Int) { println("Constructor") }}//sampleEndfun main(args: Array<String>) { Constructors(1)}
If a non-abstract class does not declare any constructors (primary or secondary), it will have a generated primary constructor with no arguments. The visibility of the constructor will be public. If you do not want your class to have a public constructor, you need to declare an empty primary constructor with non-default visibility:
class DontCreateMe private constructor () {}
NOTE: On the JVM, if all of the parameters of the primary constructor have default values, the compiler will generate an additional parameterless constructor which will use the default values. This makes it easier to use Kotlin with libraries such as Jackson or JPA that create class instances through parameterless constructors.
class Customer(val customerName: String = "")
3-Creating instances of classes
To create an instance of a class, we call the constructor as if it were a regular function:
val invoice = Invoice()val customer = Customer("Joe Smith")
Note that Kotlin does not have a new
keyword.
Creating instances of nested, inner and anonymous inner classes is described in Nested classes
.
4-Class Members
Classes can contain:
- Constructors and initializer blocks
- Functions
- Properties
- Nested and Inner Classes
- Object Declarations
2-Inheritance
All classes in Kotlin have a common superclass Any
, that is a default super for a class with no supertypes declared:
class Example // Implicitly inherits from Any
Any is not java.lang.Object
; in particular, it does not have any members other than equals(), hashCode() and toString()
. Please consult the Java interoperability
section for more details.
To declare an explicit supertype, we place the type after a colon in the class header:
open class Base(p: Int)class Derived(p: Int) : Base(p)
If the class has a primary constructor, the base type can (and must) be initialized right there, using the parameters of the primary constructor.
If the class has no primary constructor, then each secondary constructor has to initialize the base type using the super
keyword, or to delegate to another constructor which does that. Note that in this case different secondary constructors can call different constructors of the base type:
class MyView : View { constructor(ctx: Context) : super(ctx) constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)}
The open
annotation on a class is the opposite of Java’s final
: it allows others to inherit from this class. By default, all classes in Kotlin are final, which corresponds to Effective Java
, Item 17: Design and document for inheritance or else prohibit it.
1-Overriding Methods
As we mentioned before, we stick to making things explicit in Kotlin. And unlike Java, Kotlin requires explicit annotations for overridable members (we call them open) and for overrides:
open class Base { open fun v() {} fun nv() {}}class Derived() : Base() { override fun v() {}}
The override
annotation is required for Derived.v()
. If it were missing, the compiler would complain. If there is no open
annotation on a function, like Base.nv(), declaring a method with the same signature in a subclass is illegal, either with override
or without it. In a final class (e.g. a class with no open
annotation), open members are prohibited.
A member marked override
is itself open, i.e. it may be overridden in subclasses. If you want to prohibit re-overriding, use final
:
open class AnotherDerived() : Base() { final override fun v() {}}
2-Overriding Properties
Overriding properties works in a similar way to overriding methods; properties declared on a superclass that are then redeclared on a derived class must be prefaced with override
, and they must have a compatible type. Each declared property can be overridden by a property with an initializer or by a property with a getter method.
open class Foo { open val x: Int get() { ... }}class Bar1 : Foo() { override val x: Int = ...}
You can also override a val
property with a var
property, but not vice versa. This is allowed because a val
property essentially declares a getter method, and overriding it as a var
additionally declares a setter method in the derived class.
Note that you can use the override
keyword as part of the property declaration in a primary constructor.
interface Foo { val count: Int}class Bar1(override val count: Int) : Fooclass Bar2 : Foo { override var count: Int = 0}
3-Calling the superclass implementation
Code in a derived class can call its superclass functions and property accessors implementations using the super
keyword:
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}
Inside an inner class, accessing the superclass of the outer class is done with the super keyword qualified with the outer class name: 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 } }}
4-Overriding Rules
In Kotlin, implementation inheritance is regulated by the following rule: if a class inherits many implementations of the same member from its immediate superclasses, it must override this member and provide its own implementation (perhaps, using one of the inherited ones). To denote the supertype from which the inherited implementation is taken, we use super
qualified by the supertype name in angle brackets, e.g. 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() }}
It’s fine to inherit from both A
and B
, and we have no problems with a()
and b()
since C
inherits only one implementation of each of these functions. But for f()
we have two implementations inherited by C
, and thus we have to override f()
in C
and provide our own implementation that eliminates the ambiguity.
3-Abstract Classes
A class and some of its members may be declared abstract
. An abstract member does not have an implementation in its class. Note that we do not need to annotate an abstract class or function with open – it goes without saying.
We can override a non-abstract open member with an abstract one
open class Base { open fun f() {}}abstract class Derived : Base() { override abstract fun f()}
4-Companion Objects
In Kotlin, unlike Java or C#, classes do not have static methods. In most cases, it’s recommended to simply use package-level functions instead.
If you need to write a function that can be called without having a class instance but needs access to the internals of a class (for example, a factory method), you can write it as a member of an object declaration
inside that class.
Even more specifically, if you declare a companion object
inside your class, you’ll be able to call its members with the same syntax as calling static methods in Java/C#, using only the class name as a qualifier.
- Kotlin-2.1-类和继承
- Kotlin-类和继承
- Kotlin 类和继承
- Kotlin-类和继承
- Android Kotlin入门-类和继承
- Kotlin语法(六)-类和继承
- Kotlin: if表达式、类和继承
- Kotlin学习之-5.1 类和继承
- Kotlin-08.类和继承(class/Inheritance)
- Kotlin 中的 类 和 继承 (一)
- Kotlin 学习笔记(三) 类和继承
- Kotlin 中的 类 和 继承 (二)
- Kotlin基础——类和继承
- [kotlin 系列] (s2_1)类和继承
- Kotlin 第六章:类和继承
- Kotlin 类与继承
- Kotlin 学习之类和继承
- android kotlin类和对象(一)-类和继承
- 数据结构
- 关于d3生成图片的一些实例
- ffmpeg首页搜索可以查到api
- 第十四周项目2
- MFC resizer封装
- Kotlin-2.1-类和继承
- setenv,printenv,export
- 【BZOJ1257】余数之和(数论分块,暴力)
- 软件工程的相关管理
- 我们研究了100多位CEO的“法律问题”,诚意献上这一篇干货指南
- 【第十二周】688. Knight Probability in Chessboard
- HDU 4311 4312 切比雪夫距离与曼哈顿距离
- 【拜小白opencv】46-RGB直方图计算与绘制----calcHist()函数、normalize()函数
- Linux 上标准c复制文件