大家好,歡迎加入小李君的Kotlin學(xué)習(xí)之旅。今天是小李君學(xué)習(xí) Kotlin 的第八天。
寫了幾天日記,小李君發(fā)現(xiàn),其實(shí)帶著大家一起讀官方文檔應(yīng)該會(huì)更加有趣。比起自己從頭開始寫(畢竟不是漢語言文學(xué)專業(yè)畢業(yè)的,想想都覺得害怕),人工意譯官方文檔和加一些吐槽批注和學(xué)習(xí)提示應(yīng)該會(huì)讓《Kotlin學(xué)習(xí)日記》本身更具可讀性,小李君還在不斷學(xué)習(xí)探索中,謝謝大家支持。下面就開始今天的學(xué)習(xí)專題:類和繼承。
官方文檔:
Classes and Inheritance - 類和繼承
Classes - 類
Classes in Kotlin are declared using the keyword class{: .keyword }:
類在Kotlin里面就是用關(guān)鍵字 class 來聲明的,就跟其他語言一樣,除了C語言:
class Student { // 我是學(xué)生類,啦啦啦
}
class Student(name:String, age:Int) {
val myName = name
var myAge = age
}
class 類名 類頭 {
// 類體
}
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.
類的聲明由類名和類頭以及類體組成,類頭包含了形參和首要構(gòu)造器等等,類體被大括號(hào)包圍著。類頭和類體都是可寫可不寫,如果一個(gè)類沒有類體,那么大括號(hào)就可以直接省略掉。
class Student
Constructors - 構(gòu)造器 - 構(gòu)造函數(shù)
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).
一個(gè) Kotlin 的類可以有一個(gè)首要構(gòu)造器和多個(gè)次要構(gòu)造器。而首要構(gòu)造器是類頭的一部分。他通常出現(xiàn)在類名的后面。
class Person constructor(firstName: String) { // 我是首要構(gòu)造器
}
If the primary constructor does not have any annotations or visibility modifiers, the constructor{: .keyword }
keyword can be omitted:
如果首要構(gòu)造器沒有被任何注解或者修飾符所修飾。那么 constructor 這個(gè)關(guān)鍵字可以直接省略掉。感覺就像 JavaScript 的 Function 聲明的類:
// javascript
function Person(firstName) {
}
// kotlin
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 } keyword:
首要構(gòu)造器不可以包含任何初始化的代碼。而初始化的代碼可以寫在初始化代碼塊,這種代碼塊用 init {...} 表示:
class Student(name: String) {
init {
logger.info("Student initialized with value ${name}")
}
}
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:
需要注意的是,首要構(gòu)造器的形參可以直接用在初始化代碼塊上。這些形參也可以用在類體的字段的初始化聲明上。
class Student(name: String) {
val name = name.toUpperCase() // 我是字段
}
In fact, for declaring properties and initializing them from the primary constructor, Kotlin has a concise syntax:
事實(shí)上,Kotlin 針對(duì)于首要構(gòu)造器的字段的聲明和初始化代碼,發(fā)明了一個(gè)簡潔的語法糖,小李君表示非常喜歡:
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{: .keyword }) or read-only (val{: .keyword }).
首要構(gòu)造器里面可以聲明 val 常量字段和 var 變量字段。
If the constructor has annotations or visibility modifiers, the constructor{: .keyword } keyword is required, and the modifiers go before it:
如果構(gòu)造器有注解或修飾符修飾,那么就要在修飾符后面顯式地寫出 "constructor" 這個(gè)關(guān)鍵字。貌似這個(gè)前面已經(jīng)提到了,嘮叨~
class Customer public @Inject constructor(name: String) { ... }
For more details, see Visibility Modifiers.
想了解更多,請(qǐng)看 Visibility Modifiers
Secondary Constructors - 次要構(gòu)造器
The class can also declare secondary constructors, which are prefixed with constructor{: .keyword }:
類也可以聲明次要構(gòu)造器,用 constructor{...} 來寫:
class Person {
constructor(parent: Person) { // 次要構(gòu)造器參上
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 } keyword:
如果一個(gè)類已經(jīng)有一個(gè)主要構(gòu)造器,那么每個(gè)次要構(gòu)造器都需要代理這個(gè)主要構(gòu)造器,無論是直接地代理還是通過其他次要構(gòu)造器間接地代理。這種代理機(jī)制可以用 this(...) 來表示:
class Person(val name: String) {
// 次要構(gòu)造器代理調(diào)用了首要構(gòu)造器,有點(diǎn)像 C++
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
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:
如果一個(gè)非抽象類沒有聲明任何構(gòu)造器,那么這個(gè)類就會(huì)生成一個(gè)無參的主要構(gòu)造器(就像 Java 那樣)。通常這種構(gòu)造器都是 public 的。如果你不想讓一個(gè)類有 public 的無參構(gòu)造器,那么你就需要聲明 private:
// 私有的無參構(gòu)造器通常用于單例模式
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.
注意:在JVM里頭,如果主要構(gòu)造器上面所有的參數(shù)都有默認(rèn)值,那么編譯器就會(huì)生成一個(gè)額外的無參構(gòu)造器用于默認(rèn)值賦值。這樣能夠更加容易地使用 Kotlin 一些例如 Jackson 或 JPA 這些可以通過無參構(gòu)造器創(chuàng)建實(shí)體的庫。class Customer(val customerName: String = "")
Creating instances of classes - new一個(gè)對(duì)象
To create an instance of a class, we call the constructor as if it were a regular function:
直接調(diào)用構(gòu)造器函數(shù)來創(chuàng)建實(shí)體。不用 new 一個(gè):
val student = Student()
val person = Person("Joe Smith")
Note that Kotlin does not have a new{: .keyword } keyword.
Kotlin 就是沒有 new 關(guān)鍵字。不服來戰(zhàn)。
Creating instances of nested, inner and anonymous inner classes is described in Nested classes.
關(guān)于創(chuàng)建復(fù)合類,內(nèi)部類,匿名內(nèi)部類這些信息請(qǐng)看這里
Nested classes
Class Members - 類的成員
Classes can contain
類的成員有:
- Constructors and initializer blocks - 構(gòu)造器和初始化代碼塊
- Functions - 函數(shù) - 方法
- Properties - 成員變量 - 字段
- Nested and Inner Classes - 復(fù)合類和內(nèi)部類
-
Object Declarations - 對(duì)象聲明
講真,老外喜歡賣關(guān)子,思維非常跳躍。小李君建議先不管上面這些概念,以后再看,先把這章搞完。
Inheritance - 繼承
All classes in Kotlin have a common superclass Any
, that is a default super for a class with no supertypes declared:
所有的類都有一個(gè)共同的父類 Any,默認(rèn)就有了的(是不是很像 Java 的Object):
class Student // 隱式地繼承 Any
Any
is not java.lang.Object
; in particular, it does not have any members other than equals()
, hashCode()
and toString()
.
Any 類并非是 java.lang.Object;事實(shí)上,Any除了 equals(),hashcode(),toString() 以外并沒有任何成員。(沒有 wait() notify() 你懂的)。
Please consult the Java interoperability section for more details.
更多請(qǐng)看 與 Java 的愛恨情仇篇
To declare an explicit supertype, we place the type after a colon in the class header:
要顯式地聲明一個(gè)父類,就需要在類頭的冒號(hào)后面加上父類的類型:
open class Base(p: Int) // open 表示該類能夠被繼承
class Derived(p: Int) : Base(p) // 有點(diǎn)像 C++
If the class has a primary constructor, the base type can (and must) be initialized right there,using the parameters of the primary constructor.
如果一個(gè)類有主要構(gòu)造器,那么父類就必須在該類的主要構(gòu)造器那里初始化。(真的很像 C++)
If the class has no primary constructor, then each secondary constructor has to initialize the base type using the super{: .keyword } 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:
如果一個(gè)沒有主要構(gòu)造器,那么每個(gè)次要構(gòu)造器都要初始化父類,并調(diào)用父類的主要構(gòu)造器或次要構(gòu)造器。需要注意的是,不同次要構(gòu)造器可以調(diào)用不同的父類構(gòu)造器:
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
The open{: .keyword } annotation on a class is the opposite of Java's final{: .keyword }: it allows others to inherit from this class. By default, all classes in Kotlin are final, which corresponds to Effective Java, Item 17:
那個(gè) open 注解與 Java 的 final 唱反調(diào)。這么設(shè)計(jì)是有根據(jù)的,具體在 Effective Java :
Design and document for inheritance or else prohibit it.
繼承這事決不能有半點(diǎn)含糊
Overriding Methods - 復(fù)寫方法
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:
就像之前提到的,Kotlin 一切都是顯式設(shè)計(jì)。不像 Java 那樣(這黑的~),Kotlin 要求顯式地注解需要被復(fù)寫的成員(用的就是 open),例如:
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}
The override{: .keyword } annotation is required for Derived.v()
. If it were missing, the compiler would complain. If there is no open{: .keyword } annotation on a function, like Base.nv()
, declaring a method with the same signature in a subclass is illegal, either with override{: .keyword } or without it. In a final class (e.g. a class with no open{: .keyword } annotation), open members are prohibited.
override 注解在 ‘Derived.v()’ 那里是必須要寫的。如果沒寫,則會(huì)有編譯錯(cuò)誤。如果沒有 open 注解修飾在一個(gè)方法,例如 ‘Base.nv()’,那么聲明一個(gè)同名的方法于一個(gè)子類是不合法的,無論寫沒寫 open 注解。在一個(gè) final 類里面(這個(gè)類沒有修飾 open),open的成員都是被禁止復(fù)寫的。反正繼承復(fù)寫這事兒,Kotlin 都是明明白白的,不像 Java那樣糊里糊涂的(又在黑 Java ~)。
A member marked override{: .keyword } is itself open, i.e. it may be overridden in subclasses. If you want to prohibit re-overriding, use final{: .keyword }:
一個(gè)被標(biāo)記為override的成員,自身也是 open 的,即,其可以被子類所復(fù)寫。如果想要禁止這種再復(fù)寫的行為,可以使用 final 來修飾:
open class AnotherDerived() : Base() {
// AnotherDerived 的子類不可以復(fù)寫該方法了
final override fun v() {}
}
Overriding Properties - 復(fù)寫字段
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{: .keyword }, 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.
復(fù)寫字段與復(fù)寫方法一樣;字段聲明在父類,想要復(fù)寫它,就必須要在子類的字段那里修飾override,并且字段類型必須相同。每個(gè)聲明的字段可以被帶有初始化操作的字段或者有 getter 方法的字段所復(fù)寫。
這段內(nèi)容讀得有點(diǎn)難懂,畢竟字段的學(xué)習(xí)還沒展開,這里只留個(gè)心眼就行。
open class Foo {
open val x: Int get { ... } // getter
}
class Bar1 : Foo() {
override val x: Int = ... // initializer
}
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.
可以用 var 字段復(fù)寫 val 字段,但不可以反過來同理可得。之所以允許用 var 復(fù)寫 val 是因?yàn)橐粋€(gè) val 字段必須聲明一個(gè) getter 方法,而且用 var 來復(fù)寫就需要在子類中聲明一個(gè) setter 方法。
Note that you can use the override{: .keyword } keyword as part of the property declaration in a primary constructor.
注意,override 可以用在主要構(gòu)造器上。
interface Foo { // 第一次出現(xiàn)了接口
val count: Int
}
class Bar1(override val count: Int) : Foo // 實(shí)現(xiàn)了接口
class Bar2 : Foo {
override var count: Int = 0
}
Overriding Rules - 復(fù)寫的規(guī)則
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{: .keyword } qualified by the supertype name in angle brackets, e.g. super<Base>
:
在 Kotlin 的世界里,實(shí)現(xiàn)繼承是非常有規(guī)律的:如果一個(gè)類繼承父類了多個(gè)相同成員的實(shí)現(xiàn),它就必須復(fù)寫其成員,以及提供它自己的實(shí)現(xiàn)(或使用其中繼承父類的一部分實(shí)現(xiàn))。為了在一個(gè)繼承的實(shí)現(xiàn)中表示一個(gè)父類的類型,使用 super 來表示。例如 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() // 有點(diǎn)激進(jìn)啊
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.
直接繼承 A 和 B 是可以的,而且C 繼承了 a() 和 b(),沒啥問題。但是 C 的 f() 復(fù)寫了父類的 f() 。所以,要顯式地用 super 來指明調(diào)用哪個(gè)父類的 f()。
這段雖然講得復(fù)雜點(diǎn),但是看代碼還是能夠看懂的,關(guān)鍵都在 super。
Abstract Classes - 抽象類
A class and some of its members may be declared abstract{: .keyword }. 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.
一個(gè)類及其成員都可以聲明 abstract 。一個(gè)抽象的成員不可以有實(shí)現(xiàn)的代碼。要知道,修飾了 abstract 的成員不再需要修飾 open。
We can override a non-abstract open member with an abstract one
用一個(gè)抽象的成員來復(fù)寫一個(gè)非抽象且 open 成員。
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
Companion Objects - 伴隨對(duì)象(什么鬼)
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.
對(duì)于 Kotlin 來說,不像 Java 或 C#,類是不會(huì)有靜態(tài)方法的。在大多數(shù)情況下,Kotlin 更加推薦用簡單的包級(jí)函數(shù)來替代。(也許跟內(nèi)存優(yōu)化有關(guān))
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.
如果你需要寫一個(gè)函數(shù),這函數(shù)可以不用通過任何類的對(duì)象來調(diào)用但卻需要訪問一個(gè)類的內(nèi)部部分(例如一個(gè)工廠方法),你可以寫一個(gè) 對(duì)象聲明 的成員。
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.
尤其是,如果你聲明了一個(gè) 伴隨對(duì)象 于你的類,你可以直接調(diào)用它,就像 Java 或 C# 那樣調(diào)取類的靜態(tài)方法。
這里又是暈暈的,大概的意思都懂,后面會(huì)了解到伴隨對(duì)象的作用和來龍去脈。這里只是稍微提及了一下,老外的思維還是一如既往地跳躍。
今天就寫到這了,完。