大家好,歡迎加入小李君的Kotlin學(xué)習(xí)之旅西设。今天是小李君學(xué)習(xí) Kotlin 的第八天棍鳖。
寫了幾天日記炮叶,小李君發(fā)現(xiàn),其實帶著大家一起讀官方文檔應(yīng)該會更加有趣渡处。比起自己從頭開始寫(畢竟不是漢語言文學(xué)專業(yè)畢業(yè)的镜悉,想想都覺得害怕),人工意譯官方文檔和加一些吐槽批注和學(xué)習(xí)提示應(yīng)該會讓《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)造器等等馍悟,類體被大括號包圍著畔濒。類頭和類體都是可寫可不寫,如果一個類沒有類體锣咒,那么大括號就可以直接省略掉侵状。
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).
一個 Kotlin 的類可以有一個首要構(gòu)造器和多個次要構(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 這個關(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:
事實上,Kotlin 針對于首要構(gòu)造器的字段的聲明和初始化代碼鳍寂,發(fā)明了一個簡潔的語法糖改含,小李君表示非常喜歡:
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" 這個關(guān)鍵字捍壤。貌似這個前面已經(jīng)提到了,嘮叨~
class Customer public @Inject constructor(name: String) { ... }
For more details, see Visibility Modifiers.
想了解更多隔心,請看 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:
如果一個類已經(jīng)有一個主要構(gòu)造器,那么每個次要構(gòu)造器都需要代理這個主要構(gòu)造器硬霍,無論是直接地代理還是通過其他次要構(gòu)造器間接地代理帜慢。這種代理機制可以用 this(...) 來表示:
class Person(val name: String) {
// 次要構(gòu)造器代理調(diào)用了首要構(gòu)造器,有點像 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òu)造器唯卖,那么這個類就會生成一個無參的主要構(gòu)造器(就像 Java 那樣)粱玲。通常這種構(gòu)造器都是 public 的。如果你不想讓一個類有 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)值,那么編譯器就會生成一個額外的無參構(gòu)造器用于默認(rèn)值賦值橄碾。這樣能夠更加容易地使用 Kotlin 一些例如 Jackson 或 JPA 這些可以通過無參構(gòu)造器創(chuàng)建實體的庫卵沉。class Customer(val customerName: String = "")
Creating instances of classes - new一個對象
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)建實體。不用 new 一個:
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)部類這些信息請看這里
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 - 對象聲明
講真停撞,老外喜歡賣關(guān)子,思維非常跳躍悼瓮。小李君建議先不管上面這些概念戈毒,以后再看,先把這章搞完横堡。
Inheritance - 繼承
All classes in Kotlin have a common superclass Any
, that is a default super for a class with no supertypes declared:
所有的類都有一個共同的父類 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;事實上命贴,Any除了 equals()恐疲,hashcode()腊满,toString() 以外并沒有任何成員。(沒有 wait() notify() 你懂的)培己。
Please consult the Java interoperability section for more details.
更多請看 與 Java 的愛恨情仇篇
To declare an explicit supertype, we place the type after a colon in the class header:
要顯式地聲明一個父類碳蛋,就需要在類頭的冒號后面加上父類的類型:
open class Base(p: Int) // open 表示該類能夠被繼承
class Derived(p: Int) : Base(p) // 有點像 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ò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òu)造器肃弟,那么每個次要構(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:
那個 open 注解與 Java 的 final 唱反調(diào)。這么設(shè)計是有根據(jù)的敌蜂,具體在 Effective Java :
Design and document for inheritance or else prohibit it.
繼承這事決不能有半點含糊
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è)計。不像 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()’ 那里是必須要寫的。如果沒寫秸脱,則會有編譯錯誤落包。如果沒有 open 注解修飾在一個方法,例如 ‘Base.nv()’摊唇,那么聲明一個同名的方法于一個子類是不合法的咐蝇,無論寫沒寫 open 注解。在一個 final 類里面(這個類沒有修飾 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 }:
一個被標(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,并且字段類型必須相同檀蹋。每個聲明的字段可以被帶有初始化操作的字段或者有 getter 方法的字段所復(fù)寫松申。
這段內(nèi)容讀得有點難懂云芦,畢竟字段的學(xué)習(xí)還沒展開,這里只留個心眼就行贸桶。
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 是因為一個 val 字段必須聲明一個 getter 方法皇筛,而且用 var 來復(fù)寫就需要在子類中聲明一個 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 // 實現(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 的世界里旗笔,實現(xiàn)繼承是非常有規(guī)律的:如果一個類繼承父類了多個相同成員的實現(xiàn),它就必須復(fù)寫其成員拄踪,以及提供它自己的實現(xiàn)(或使用其中繼承父類的一部分實現(xiàn))蝇恶。為了在一個繼承的實現(xiàn)中表示一個父類的類型,使用 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() // 有點激進(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)用哪個父類的 f()。
這段雖然講得復(fù)雜點忿薇,但是看代碼還是能夠看懂的裙椭,關(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.
一個類及其成員都可以聲明 abstract 署浩。一個抽象的成員不可以有實現(xiàn)的代碼揉燃。要知道,修飾了 abstract 的成員不再需要修飾 open筋栋。
We can override a non-abstract open member with an abstract one
用一個抽象的成員來復(fù)寫一個非抽象且 open 成員炊汤。
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
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.
對于 Kotlin 來說,不像 Java 或 C#弊攘,類是不會有靜態(tài)方法的抢腐。在大多數(shù)情況下,Kotlin 更加推薦用簡單的包級函數(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.
如果你需要寫一個函數(shù)迈倍,這函數(shù)可以不用通過任何類的對象來調(diào)用但卻需要訪問一個類的內(nèi)部部分(例如一個工廠方法),你可以寫一個 對象聲明 的成員捣域。
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.
尤其是啼染,如果你聲明了一個 伴隨對象 于你的類宴合,你可以直接調(diào)用它,就像 Java 或 C# 那樣調(diào)取類的靜態(tài)方法迹鹅。
這里又是暈暈的卦洽,大概的意思都懂,后面會了解到伴隨對象的作用和來龍去脈徒欣。這里只是稍微提及了一下逐样,老外的思維還是一如既往地跳躍。
今天就寫到這了打肝,完脂新。