一、繼承(Inheritance)
-
1.1、類繼承
- 值類型(枚舉、結(jié)構(gòu)體) 不支持繼承,只有 類 支持繼承;
- 沒有父類的類稱為 基類 (Swift 并沒有像OC/Java 那樣規(guī)定 :任何類 最終都要繼承于某個基類)
- 子類可以重寫父類的
下標(biāo)、方法、屬性重寫必須加上override關(guān)鍵字
-
1.2、重寫實例 方法 和 下標(biāo)
class Animal { func speak() { print("Animal speak") } subscript(index: Int) -> Int { return index } } var animal: Animal animal = Animal() animal.speak() // 打?。?Animal speak print(animal[10]) // 打印: 10 class Cat:Animal { override func speak() { super.speak() print("Animal speak") } override subscript(index: Int) -> Int { return super[index] + 1 } } animal = Cat() animal.speak() print(animal[10]) // 打印 // Animal speak // Cat speak // 11 -
1.3、重寫類型 方法 和 下標(biāo)
- 被
class修飾的 類型 方法、下標(biāo),允許被子類重寫 - 被
static修飾的 類型 方法、下標(biāo),不允許被子類重寫
- 提示:被
static修飾的 類型 方法、下標(biāo),不允許被子類重寫
- 被
-
1.4、重寫屬性
- 子類可以將父類的屬性(存儲、計算)重寫為
計算屬性 - 子類
不可以將父類屬性重寫為存儲屬性 - 只能重寫
var屬性,不能重寫let屬性 - 重寫時,屬性名、類型要一致
- 子類重寫后的屬性權(quán)限 不能小于 父類屬性的權(quán)限
- 如果父類屬性是只讀的,那么子類重寫后的屬性可以是只讀的、也可以是可讀寫的
- 如果父類屬性是可讀寫的,那么子類重寫后的屬性也必須是可讀寫的
- 子類可以將父類的屬性(存儲、計算)重寫為
-
1.5、重寫實例屬性
class Circle { var radius: Int = 0 var diameter: Int { set { print("Circle setDiameter") radius = newValue / 2 } get { print("Circle getDiameter") return radius * 2 } } } var circle:Circle circle = Circle() circle.radius = 6 // Circle getDiameter // 12 print(circle.diameter) // Circle setDiameter circle.diameter = 20 // 10 print(circle.radius) class SubCircle : Circle { override var radius: Int { set { print("SubCircle setRadius") super.radius = newValue > 0 ? newValue : 0 } get { print("SubCircle getRadius") return super.radius } } override var diameter: Int { set { print("SubCircle setDiameter") super.diameter = newValue > 0 ? newValue : 0 } get { print("SubCircle getDiameter") return super.diameter } } } circle = SubCircle() // SubCircle setRadius circle.radius = 6 // SubCircle getDiameter // Circle getDiameter // SubCircle getRadius // 12 print("--------------") print(circle.diameter) // SubCircle setDiameter // Circle setDiameter // SubCircle setRadius print("--------------") circle.diameter = 20 // SubCircle getRadius // 10 print("--------------") print(circle.radius) -
1.6、重寫類型屬性
- 被 class 修飾的
計算型屬性,可以被子類重寫 - 被 static 修飾的
類型屬性(存儲、計算),不可以被子類重寫
被 static 修飾的 類型屬性(存儲、計算),不可以被子類重寫 - 被 class 修飾的
-
1.7、屬性觀察器
可以在子類中為父類屬性(除了
只讀計算屬性、let屬性)增加屬性觀察器-
舉例一
class Circle { var radius: Int = 1 } class SubCircle : Circle { override var radius: Int { willSet { print("SubCircle willSetRadius", newValue) } didSet { print("SubCircle didSetRadius", oldValue, radius) } } } var circle = SubCircle() // SubCircle willSetRadius 10 // SubCircle didSetRadius 1 10 circle.radius = 10 -
舉例二
class Circle { var radius: Int = 1 { willSet { print("Circle willSetRadius", newValue) } didSet { print("Circle didSetRadius", oldValue, radius) } } } class SubCircle : Circle { override var radius: Int { willSet { print("SubCircle willSetRadius", newValue) } didSet { print("SubCircle didSetRadius", oldValue, radius) } } } var circle = SubCircle() // SubCircle willSetRadius 10 // Circle willSetRadius 10 // Circle didSetRadius 10 // SubCircle didSetRadius 1 10 circle.radius = 10 -
舉例三
class Circle { var radius: Int { set { print("Circle setRadius", newValue) } get { print("Circle getRadius") return 20 } } } class SubCircle : Circle { override var radius: Int { willSet { print("SubCircle willSetRadius", newValue) } didSet { print("SubCircle didSetRadius", oldValue, radius) } } } var circle = SubCircle() // Circle getRadius // SubCircle willSetRadius 10 // Circle setRadius 10 // Circle getRadius // SubCircle didSetRadius 20 20 circle.radius = 10 -
舉例四
class Circle { class var radius: Int { set { print("Circle setRadius", newValue) } get { print("Circle getRadius") return 20 } } } class SubCircle : Circle { override static var radius: Int { willSet { print("SubCircle willSetRadius", newValue) } didSet { print("SubCircle didSetRadius", oldValue, radius) } } } Circle getRadius SubCircle willSetRadius 10 Circle setRadius 10 Circle getRadius SubCircle didSetRadius 20 20
1.8、final:被
final修飾的的下標(biāo)、方法、屬性,禁止被重寫;被final修飾的類禁止被繼承
二、初始化
-
2.1、初始化器
類、結(jié)構(gòu)體、枚舉都可以定義初始化器-
類有2種初始化器:指定初始化器(designated initializer)、便捷初始化器(convenience initializer)
// 指定初始化器 init(parameters) { statements } // 便捷初始化器 convenience init(parameters) { statements } 每個類至少有一個指定初始化器,指定初始化器是類的主要初始化器
默認初始化器總是類的指定初始化器
類偏向于少量指定初始化器,一個類通常只有一個指定初始化器,可以有多個便捷初始化器
-
初始化器的相互調(diào)用規(guī)則
- 指定初始化器必須從它的直系父類調(diào)用指定初始化器 ,也就是是能調(diào)用父類的指定初始化器
- 便捷初始化器必須從相同的類里調(diào)用另一個初始化器 ,也就是不能調(diào)用父類 初始化器
- 便捷初始化器最終必須調(diào)用一個指定初始化器
-
2.2、初始化器的相互調(diào)用
Designated代表指定初始化器
分析:這一套規(guī)則保證了:使用任意初始化器,都可以完整地初始化實例
- Swift在編碼安全方面是煞費苦心,為了保證初始化過程的安全,設(shè)定了兩段式初始化、 安全檢查
- 兩段式初始化
- 第1階段:初始化所有存儲屬性
- <1>、外層調(diào)用指定\便捷初始化器
- <2>、分配內(nèi)存給實例,但未初始化
- <3>、指定初始化器確保當(dāng)前類定義的存儲屬性都初始化
- <4>、指定初始化器調(diào)用父類的初始化器,不斷向上調(diào)用,形成初始化器鏈
- 第2階段:設(shè)置新的存儲屬性值
- <1>、從頂部初始化器往下,鏈中的每一個指定初始化器都有機會進一步定制實例
- <2>、初始化器現(xiàn)在能夠使用self(訪問、修改它的屬性,調(diào)用它的實例方法等等) 3 最終,鏈中任何便捷初始化器都有機會定制實例以及使用self
-
2.3、安全檢查
-
指定初始化器必須保證在調(diào)用父類初始化器之前,其所在類定義的所有存儲屬性都要初始化完成,如下 代碼:
super.init(age: 2)必須放到self.name = name的后面class Person { var age:Int init(age:Int) { self.age = age } } class Son : Person { var name:String init(name:String) { self.name = name super.init(age: 2) } } -
指定初始化器必須先調(diào)用父類初始化器,然后才能為繼承的屬性設(shè)置新值,如下 代碼:
super.init(age: 2)必須放到self.age = 20的前面class Person { var age:Int init(age:Int) { self.age = age } } class Son : Person { var name:String init(name:String) { self.name = name super.init(age: 2) self.age = 20 } } 便捷初始化器必須先調(diào)用同類中的其它初始化器,然后再為任意屬性設(shè)置新值
初始化器在第1階段初始化完成之前,不能調(diào)用任何實例方法、不能讀取任何實例屬性的值,也不能引用self
直到第1階段結(jié)束,實例才算完全合法,也就是可以用
self
-
-
2.4、重寫:方法名、參數(shù)名以及 類型和父類的一樣
-
當(dāng)重寫父類的指定初始化器時,必須加上override(即使子類的實現(xiàn)是便捷初始化器),如下 代碼
class Person { var age:Int init(age:Int) { self.age = age } } class Son : Person { override init(age:Int) { super.init(age: 2) } } -
如果子類寫了一個匹配父類便捷初始化器的初始化器,不用加上override ,因為父類的便捷初始化器永遠不會通過子類直接調(diào)用,因此,嚴格來說,子類無法重寫父類的便捷初始化器,如下代碼,我們可以看到子類 和 父類 都有
convenience init(age:Int,no:Int),子類的便利方法前不需要加override,因為便利方法是橫向調(diào)用的,也就是只能調(diào)用自己類里面的方法class Person { var age:Int init(age:Int) { self.age = age } convenience init(age:Int,no:Int) { self.init(age:20) } } class Son : Person { init() { super.init(age: 20) } convenience init(age:Int,no:Int) { self.init() } }
-
-
2.5、自動繼承
-
(1)、如果子類沒有自定義任何指定初始化器,它會自動繼承父類所有的指定初始化器 ,如下代碼:Son 會繼承Person的所有方法
class Person { var age:Int init(age:Int) { self.age = age } convenience init(age:Int,no:Int) { self.init(age:20) } } class Son : Person { } (2)、如果子類提供了父類所有指定初始化器的實現(xiàn)(要么通過方式1繼承,要么重寫),子類自動繼承所有的父類便捷初始化器
-
(3)、就算子類添加了更多的便捷初始化器,這些規(guī)則仍然適用,如下
class Person { var age:Int init(age:Int) { self.age = age } convenience init(age:Int,no:Int) { self.init(age:20) } } class Son : Person { convenience init(age:Int,no:Int) { self.init(age:20) } func test() -> Void { print("測試") } } let son = Son(age: 20) son.test() -
(4)、子類以便捷初始化器的形式重寫父類的指定初始化器,也可以作為滿足規(guī)則2的一部分,如下代碼
class Person { var age:Int init(age:Int) { self.age = age } convenience init(age:Int,no:Int) { self.init(age:20) } } class Son : Person { override init(age:Int) { super.init(age: age) } } // 調(diào)用:可以看到父類 的便利方法被繼承了下來 let son = Son(age: 10, no: 20)
-
-
2.6、required
用required修飾指定初始化器,表明其所有子類都必須實現(xiàn)該初始化器(通過繼承或者重寫實現(xiàn))
-
如果子類重寫了required初始化器,也必須加上required,不用加override
class Person { required init() { } init(age: Int) { } } class Student : Person { required init() { super.init() } }
-
2.7、屬性觀察器
-
父類的屬性在它自己的初始化器中賦值不會觸發(fā)屬性觀察器,但在子類的初始化器中賦值會觸發(fā)屬性觀察器
class Person { var age: Int { willSet { print("willSet", newValue) } didSet { print("didSet", oldValue, age) } } init() { self.age = 0 } } class Student : Person { override init() { super.init() self.age = 1 // 觸發(fā)屬性監(jiān)聽器 } } // willSet 1 // didSet 0 1 var stu = Student()
-
-
2.8、可失敗的初始化器
-
類、結(jié)構(gòu)體、枚舉都可以使用init?定義可失敗初始化器 n 之前接觸過的可失敗初始化器
class Person { var name: String init?(name: String) { if name.isEmpty { return nil } self.name = name } }之前接觸過的可失敗初始化器
var num = Int("123") public init?(_ description: String) enum Answer : Int { case wrong, right } var an = Answer(rawValue: 1) -
不允許同時定義參數(shù)標(biāo)簽、參數(shù)個數(shù)、參數(shù)類型相同的可失敗初始化器和非可失敗初始化器,如下代碼,
init?和init是不被允許的class Person { var name: String init?(name: String) { if name.isEmpty { return nil } self.name = name } init(name: String) { self.name = name } } // 調(diào)用產(chǎn)生歧義,不知道調(diào)用的是哪個 init var p1 = Person(name:"") var p2 = Person(name:"Tom") 可以用init!定義隱式解包的可失敗初始化器
可失敗初始化器可以調(diào)用非可失敗初始化器,非可失敗初始化器調(diào)用可失敗初始化器需要進行解包
-
如果初始化器調(diào)用一個可失敗初始化器導(dǎo)致初始化失敗,那么整個初始化過程都失敗,并且之后的代碼都停止執(zhí)行,如下代碼
class Person { var name:String init?(name:String) { if name.isEmpty { return nil } self.name = name } convenience init?() { self.init(name:"") // 失敗之后,后面的代碼就不再執(zhí)行 self.name = "Tom" // ....... } } -
可以用一個非可失敗初始化器重寫一個可失敗初始化器,但反過來是不行的
class Person { var name:String init?(name:String) { if name.isEmpty { return nil } self.name = name } convenience init?() { self.init(name:"") // 失敗之后,后面的代碼就不再執(zhí)行 self.name = "Tom" // ....... } } class Student : Person { override init(name: String) { } }
-
-
2.9、反初始化器(deinit)
-
deinit叫做反初始化器,類似于C++的析構(gòu)函數(shù)、OC中的dealloc方法,當(dāng)類的實例對象被釋放內(nèi)存時,就會調(diào)用實例對象的deinit方法class Person { deinit { print("Person對象銷毀了") } } deinit不接受任何參數(shù),不能寫小括號,不能自行調(diào)用
父類的deinit能被子類繼承
子類的deinit實現(xiàn)執(zhí)行完畢后會調(diào)用父類的deinit
-
三、可選鏈(Optional Chaining)
如果可選項為nil,調(diào)用方法、下標(biāo)、屬性失敗,結(jié)果為nil
-
如果可選項不為nil,調(diào)用方法、下標(biāo)、屬性成功,結(jié)果會被包裝成可選項;如果結(jié)果本來就是可選項,不會進行再次包裝
class Car { var price = 0 } class Dog { var weight = 0 } class Person { var name: String = "" var dog: Dog = Dog() var car: Car? = Car() func age() -> Int { 18 } func eat() { print("Person eat") } subscript(index: Int) -> Int { index } } var person: Person? = Person() var age1 = person!.age() // Int var age2 = person?.age() // Int? var name = person?.name // String? var index = person?[6] // Int? if let _ = Person?.eat() { print("eat調(diào)用成功") } esle { print("eat調(diào)用失敗") } -
如果多個
?可以鏈接在一起,如果鏈中任何一個節(jié)點是nil,那么整個鏈就會調(diào)用失敗,如下面的var price = person?.car?.price,當(dāng)person為nil的 時候就不會再調(diào)用car?var dog = person?.dog // Dog? var weight = person?.dog.weight // Int? var price = person?.car?.price // Int? -
可選鏈的使用
var scores = ["Jack": [86, 82, 84], "Rose": [79, 94, 81]] scores["Jack"]?[0] = 100 scores["Rose"]?[2] += 10 scores["Kate"]?[0] = 88 var num1: Int? = 5 num1? = 10 // Optional(10) var num2: Int? = nil num2? = 10 // nil var dict: [String : (Int, Int) -> Int] = [ "sum" : (+), "difference" : (-) ] var result = dict["sum"]?(10, 20) // Optional(30), Int?提示
- 當(dāng)
scores["Jack"]為nil的 時候就不會再去調(diào)用[0] - num1? = 10 也一樣,當(dāng)num1為nil的時候,就不需要 10 再賦值
-
var dict: [String : (Int, Int) -> Int]里面的(+)和(-)代表兩個整數(shù)的相加或者相減
- 當(dāng)
四、協(xié)議(Protocol)
-
4.1、協(xié)議可以用來定義方法、屬性、下標(biāo)的聲明,協(xié)議可以被枚舉、結(jié)構(gòu)體、類遵守(多個協(xié)議之間用逗號隔開)
protocol Test1 {} protocol Test2 {} protocol Test3 {} class TestClass : Test1, Test2, Test3 {}協(xié)議中定義方法時不能有默認參數(shù)值
-
默認情況下,協(xié)議中定義的內(nèi)容必須全部都實現(xiàn)(也有辦法辦到只實現(xiàn)部分內(nèi)容)
protocol Drawable { func draw() var x: Int { get set } var y: Int { get } subscript(index: Int) -> Int { get set } }
-
4.2、協(xié)議中的屬性
protocol Drawable { func draw() var x: Int { get set } var y: Int { get } subscript(index: Int) -> Int { get set } }協(xié)議中定義屬性時必須用var關(guān)鍵字
實現(xiàn)協(xié)議時的屬性權(quán)限要不小于協(xié)議中定義的屬性權(quán)限
協(xié)議定義get、set,用var存儲屬性或get、set計算屬性去實現(xiàn)
-
協(xié)議定義get,用任何屬性都可以實現(xiàn)
class Person : Drawable { var x: Int = 0 let y: Int = 0 // 因為是只讀 get ,所以用 let 也可以 func draw() { print("Person draw") } subscript(index: Int) -> Int { set {} get { index } } } class Person : Drawable { var x: Int { get { 0 } set {} } var y: Int { 0 } func draw() { print("Person draw") } subscript(index: Int) -> Int { set {} get { index } } }
-
4.3、static、class
為了保證通用,協(xié)議中必須用static定義類型方法、類型屬性、類型下標(biāo),因為 class 定義的只能在類里面使用,static定義的在類 、枚舉、結(jié)構(gòu)體 里面都可以使用protocol Drawable { static func draw() } class Person1 : Drawable { class func draw() { print("Person1 draw") } } class Person2 : Drawable { static func draw() { print("Person2 draw") } } 4.4、mutating
-
只有將協(xié)議中的實例方法標(biāo)記為mutating
才允許結(jié)構(gòu)體、枚舉的具體實現(xiàn)修改自身內(nèi)存
-
類在實現(xiàn)方法時不用加mutating,枚舉、結(jié)構(gòu)體才需要加mutating
protocol Drawable { mutating func draw() } class Size : Drawable { var width: Int = 0 func draw() { width = 10 } } struct Point : Drawable { var x: Int = 0 mutating func draw() { x = 10 } }
-
4.5、init
-
協(xié)議中還可以定義初始化器init,非final類實現(xiàn)時必須加上required
protocol Drawable { init(x: Int, y: Int) } class Point : Drawable { required init(x: Int, y: Int) {} } final class Size : Drawable { init(x: Int, y: Int) {} } -
如果從協(xié)議實現(xiàn)的初始化器,剛好是重寫了父類的指定初始化器 ,那么這個初始化必須同時加required、override
protocol Livable { init(age: Int) } class Person { init(age: Int) {} } class Student : Person, Livable { required override init(age: Int) { super.init(age: age) } }提示:required 是協(xié)議的要求,非final類實現(xiàn)時必須加上required;override 是重寫了父類的指定初始化器
-
-
4.6、init、init?、init!
協(xié)議中定義的init?、init!,可以用init、init?、init!去實現(xiàn)
-
協(xié)議中定義的init,可以用init、init!去實現(xiàn)
protocol Livable { init() init?(age: Int) init!(no: Int) } class Person : Livable { required init() {} // required init!() {} required init?(age: Int) {} // required init!(age: Int) {} // required init(age: Int) {} required init!(no: Int) {} // required init?(no: Int) {} // required init(no: Int) {} }
-
4.7、協(xié)議的繼承
一個協(xié)議可以繼承其他協(xié)議protocol Runnable { func run() } protocol Livable : Runnable { func breath() } class Person : Livable { func breath() {} func run() {} } -
4.8、協(xié)議組合
協(xié)議組合,可以包含1個類類型(最多1個)protocol Livable {} protocol Runnable {} class Person {} // 接收Person或者其子類的實例 func fn0(obj: Person) {} // 接收遵守Livable協(xié)議的實例 func fn1(obj: Livable) {} // 接收同時遵守Livable、Runnable協(xié)議的實例 func fn2(obj: Livable & Runnable) {} // 接收同時遵守Livable、Runnable協(xié)議、并且是Person或者其子類的實例 func fn3(obj: Person & Livable & Runnable) {} typealias RealPerson = Person & Livable & Runnable // 接收同時遵守Livable、Runnable協(xié)議、并且是Person或者其子類的實例 func fn4(obj: RealPerson) {} -
4.9、CaseIterable
讓枚舉遵守CaseIterable協(xié)議,可以實現(xiàn)遍歷枚舉值enum Season : CaseIterable { case spring, summer, autumn, winter } let seasons = Season.allCases // 返回數(shù)組 print(seasons.count) // 4 for season in seasons { print(season) } // spring summer autumn winter提示:Season.allCases 等價于 [Season.spring,Season. summer,Season. autumn,Season. winter]
-
4.10、CustomStringConvertible
遵守CustomStringConvertible、CustomDebugStringConvertible協(xié)議,都可以自定義實例的打印字符串class Person : CustomStringConvertible, CustomDebugStringConvertible { var age = 0 var description: String { "person_\(age)" } var debugDescription: String { "debug_person_\(age)" } } var person = Person() print(person) // person_0 debugPrint(person) // debug_person_0- print調(diào)用的是
CustomStringConvertible協(xié)議的 description - debugPrint、po調(diào)用的是
CustomDebugStringConvertible協(xié)議的debugDescription
- print調(diào)用的是
-
4.11、Any、AnyObject
Swift提供了2種特殊的類型:Any、AnyObject
Any:可以代表任意類型(枚舉、結(jié)構(gòu)體、類,也包括函數(shù)類型)
-
AnyObject:可以代表任意類類型(在協(xié)議后面寫上: AnyObject代表只有類能遵守這個協(xié)議)
在協(xié)議后面寫上: class也代表只有類能遵守這個協(xié)議
var stu: Any = 10 stu = "Jack" stu = Student()創(chuàng)建1個能存放任意類型的數(shù)組
// var data = Array<Any>() var data = [Any]() data.append(1) data.append(3.14) data.append(Student()) data.append("Jack") data.append({ 10 })
-
4.12、is、as?、as!、as
is用來判斷是否為某種類型,as用來做強制類型轉(zhuǎn)換protocol Runnable { func run() } class Person {} class Student : Person, Runnable { func run() { print("Student run") } func study() { print("Student study") } } var stu: Any = 10 (stu as? Student)?.study() // 沒有調(diào)用study stu = Student() (stu as? Student)?.study() // Student study 提示:第一個問號代表轉(zhuǎn)換可能成功也可能失敗,返回可選類型;第二個問號是可選鏈 (stu as! Student).study() // Student study (stu as? Runnable)?.run() // Student run var data = [Any]() data.append(Int("123") as Any) var d = 10 as Double print(d) // 10.0 var stu: Any = 10 print(stu is Int) // true stu = "Jack" print(stu is String) // true stu = Student() print(stu is Person) // true print(stu is Student) // true print(stu is Runnable) // true -
4.13、X.self、X.Type、AnyClass
X.self 是一個元類型(metadata)的指針,metadata存放著類型相關(guān)信息
-
X.self 屬于 X.Type 類型
class Person {} class Student : Person {} var perType: Person.Type = Person.self var stuType: Student.Type = Student.self perType = Student.self var anyType: AnyObject.Type = Person.self anyType = Student.self public typealias AnyClass = AnyObject.Type var anyType2: AnyClass = Person.self anyType2 = Student.self var per = Person() var perType = type(of: per) // Person.self print(Person.self == type(of: per)) // true
-
4.14、元類型的應(yīng)用
-
元類型的應(yīng)用一
class Animal { required init() {} } class Cat : Animal {} class Dog : Animal {} class Pig : Animal {} func create(_ clses: [Animal.Type]) -> [Animal] { var arr = [Animal]() for cls in clses { arr.append(cls.init()) } return arr } print(create([Cat.self, Dog.self, Pig.self])) -
元類型的應(yīng)用二
import Foundation class Person { var age: Int = 0 } class Student : Person { var no: Int = 0 } print(class_getInstanceSize(Student.self)) // 32 print(class_getSuperclass(Student.self)!) // Person print(class_getSuperclass(Person.self)!) // Swift._SwiftObject從結(jié)果可以看得出來,Swift還有個隱藏的基類:Swift._SwiftObject ,可以參考Swift源碼
-
-
4.15、Self
-
Self 代表當(dāng)前類型
class Person { var age = 1 static var count = 2 func run() { print(self.age) // 1 print(Self.count) // 2 } } -
Self 一般用作返回值類型,限定返回值跟方法調(diào)用者必須是同一類型(也可以作為參數(shù)類型)
protocol Runnable { func test() -> Self } class Person : Runnable { required init() {} func test() -> Self { type(of: self).init() } } class Student : Person {} var p = Person() // Person print(p.test()) var stu = Student() // Student print(stu.test())
-



