Swift vs. Kotlin 漫谈之扩展篇

来源:互联网 发布:披甲龙龟事件知乎 编辑:程序博客网 时间:2024/06/07 23:31

Swift vs. Kotlin 漫谈之扩展篇

转载自公众号 KotlinThree 里的一篇文章:https://mp.weixin.qq.com/s?__biz=MzU3MDA3NzMzOA==&mid=2247483719&idx=1&sn=9b4b3ab235777689aee0d6b4274da8bc&chksm=fcf5b8b2cb8231a41038d8820672c4e99039e4126597a7819efe8aeecd75015894cf918ad987&mpshare=1&scene=1&srcid=0703QSVj40BLsRG7R8BTtIyy#rd

引子

今年Google I/O 大会上最让广大Android开发者振奋的消息就是Kotlin了。虽然很早之前就关注了,但是由于工作太忙一直没有时间(其实是懒癌犯了)学习一下。现在成为了官方支持的开发语言,再不好好学学就真的说不过去了。
机缘巧合参加了沪江网举办的一场关于Kotlin开发Android的技术沙龙。他们团队在这个领域已经有了两年的技术积累,分享了很多非常实用的知识点与技巧。整个活动下来,我深深的感觉到学习这门语言给Android开发带来的效率上的提升与思维方式上的改变,等于是打开了一个新的局面。
活动负责人安利了自己开办的一个学习Kotlin的公众号:KotlinThree,以及对比同为移动端开发新语言的Swift的计划 Swift vs Kotlin。我当时就决定参与进来,跟优秀的人一起学习总是最快的,不是吗?
于是就有了这篇文章,跟志平同学第一次合作,而且Kotlin的知识掌握的也不是很扎实,所以写的一般。但毕竟是一个好的开始,希望后面能坚持下去。

Swift -vs- Kotlin: 扩展篇

脱单秘诀

Swift:

Kotlin 君,我看你最近加班多,都快没有时间谈恋爱了?你和你的女神近况怎么样?

Kotlin

Swift君,别闹。我们只是朋友关系;

Swift

傻子都看得出来你对你的女神有意思,看你单身20余载,恋爱经验匮乏,要不要我传授你撩妹秘诀?

Kotlin

懂我者Swift君也,快说说看;

Swift

以我们Swift为例,我们先定义一个人类,然后你就是kotliner和你女神angel

class Human {    var name: String    var age: Int    var lover: Human?    init(_ name:String, age: Int) {        self.name = name        self.age = age    }}var kotliner = Human.init("Kotliner", age: 26)var angel = Human.init("Angel", age: 27)

Kotlin

先定义一个Human是吧,好,我也完成了。

class Human(name:String){    var lover: Human?    constructor(name:String ,age: Int) : this(name){    }}val kotliner = Human("Kotliner", age: 26)val angel = Human("Angel", age: 27)

Swift

然后:第一步,我教你先识别你的女神是不是单身, extension 就可以给 Human 添加一个是否单身的属性(计算性属性)

//note: Swift扩展只能是计算型属性,也必须使用var修饰,且不能赋值操作extension Human {    var isSingle: Bool {        return lover == nil    }}let angelIsSingle = angel.isSingle // true

你看, 恭喜 恭喜 ,女神还单身,你有机会是吧!

Kotlin

这TMD也可以啊,看我也现学现卖一下。是否单身的属性我们也可以加,就是写法不太一样;

fun Human.isSingle Boolean {    return this.lover == null}val angelIsSingle = angel.isSingle // true

Swift

哎呦不错哦,看样子Kotlin君天资聪慧,不久就可以脱单了;
再来:我看你女神比较高冷,试试使用撩妹秘籍,通过Extension,让Human学会一个陷入爱河的方法(func)
如果是你来撩的话

extension Human {    func fallInLoveBy撩X秘籍(_ lover: Human) {        self.lover = lover;    }}angel.fallInLoveBy撩X秘籍(kotliner)let isSingle = angel.isSingle // falselet angelsLover = angel.lover?.name // Kotliner

看,你的女神不是单身了,而且爱上你了;

当然,只要你博爱,在Swift里甚至可以让全世界男女都爱上你

//note: 类方法扩展,未实例对象extension Human {    class func perfectLoversName() -> String {        return "Kotliner"    }}var name = Human.perfectLoversName() // Kotliner

Kotlin

这招狠啊,我也试试看看,有没有觉得我们的写法要更简洁点:

fun Human.fallInLoveByX秘籍(lover: Human){    this.lover = lover}angel.fallInLoveByX秘籍(kotliner)val isSingle = angel.isSingle // falseval angelsLover = angel.lover?.name // Kotliner

Swift

是啊,厉害厉害。

Kotlin

我还悟出了其他的撩妹技巧,你也看看你以前用过没有;

fun Human.壁咚(lover: Human){    this.lover = null    lover = null}kotliner.壁咚(angel)  //卒......

Swift

哈哈哈哈哈,强行壁咚的下场太可怕了,我们也一样只是换成nil,但这个扩展是同源的,和前面一样的。
我还有一招献上

Kotlin

霸王硬上弓?

Swift

那不是你卒,是你两全卒。
回正题,你知道以前我们父母他们那一辈有媒妁之约,男孩女孩一出生就婚定一生是吧!前面一大堆的扩展学习太麻烦都可以省去,我直接在你女神出生那一刻就给你定娃娃亲,这事就简单了;(构造器)

当你的女神还是一个baby的时候,订个娃娃亲就好简单了

// note: 构造器扩展只能用便利构造器 详见 《类与继承》 漫谈篇extension Human {    // 刚刚出生 age=0    convenience init(birthWithName name: String, lover: Human?) {        self.init(name, age: 0)        self.lover = lover    }}// Angelababy(感情你女神是杨颖啊)var angelBaby = Human.init(birthWithName: "Angel", lover: kotliner)

哈哈哈,一下子就成了。

Kotlin

这个好,你们可以事后诸葛,但我们貌似是没法做构造器扩展的,倒是可以在创建类时添加一个的构造器:

class Human(name:String){    ...    constructor(name:String ,lover: Human,age: Int = 0) : this(name){    }}var angelBaby = Human(name = "Angel", lover = kotliner)

试了这么多方法有好有坏,都这么快就学会了,女神我算是已经追求到手了,我打算明天求婚。

Swift

等等等一下。。好不容易追到你女神,你要爱惜她 尊重她 安慰她 保护着她,俩人同心建立起美满的家庭。。。
你愿意这样做吗?

Kotlin

Yes, I do

Swift

好在你是程序员,物质基础比较牢靠,结婚后需要有一份稳定的工作做保证;

Kotlin

是是是,我这边公司加班太多了,以后都没法陪老婆孩子,想换一家工作时间分配比较合理一点的公司,你给我推荐一下吧;

Swift

好,这份我们公司的程序员工作合同,签了它就可以过来上班了

protocol DeveloperJobProtocol {    var job:String { get }    var salary: Int { get }    var level: Int { get }    var childSex: Sex { get }}

签合同很简单,看我给你演示一下

enum Sex {    case male, female}extension Human: DeveloperJobProtocol {    var job:String {        return "Andriod"    }    var salary: Int {        return level*2000 + 6000    }    var level: Int {        return 6    }    var childSex: Sex {        return .female    }}kotliner.salary // 18k

这是我们公司根据你的情况给你做的合同

Kotlin

18k,很好。知足了

enum class Sex {    male,female}val Human.job: Stringget():String {     return "Andriod"    }var Human.salary: Intget():Int {     return level*2000 + 6000    }var Human.level: Intget():Int {     return 6    }var Human.childSex: Sexget():Int {     return Sex.female    }kotliner.salary // 18k

合同我签了,但 childSex 是什么鬼?

Swift

你做了程序员,那你不就注定生女儿了嘛。我都想象到未来你们生女儿一起生活的画面了!(扩展嵌套类型)

extension Human {    class Child: Human {        var sex:Sex = kotliner.childSex        weak var father: Human? = kotliner        weak var mother: Human? = kotliner.lover    }    var daughter: Child {        return Child.init("littleAngel", age: 0)    }}let yourChild = kotliner.daughterlet childFatherName = yourChild.father?.name // Kotliner

Kotlin

哇!!!当程序员居然有这个福利。我喜欢生女儿,生女儿好;可是为什么我女儿的父亲母亲要用weak

Swift

你傻啊,你女儿终有一天会嫁出去的,不用weak的话就和你循环引用释放不了,你还想留你女儿一辈子?但是Human的lover我没有加weak,就是故意让你们相互引用,相守一身不离不弃;

Kotlin

我。。。(感动ing)

Swift

好了好了,你先关注当下
婚礼的事情只欠东风,你这还没有结婚就有孩子了,明儿求婚还不是百分百搞定;

Kotlin

Swift君,多谢兄弟帮忙;婚礼的事情也希望请你多多指教,我没有经验;

Swift

其实都差不多了,就差一本结婚证,你们能够遵守婚姻协议后这事就成了

// 婚姻协议protocol MarriageProtocol {    var promise:String { get }    var approveByGovernment: Bool {get}}// 这协议的内容我已经限制人类身上了,动物结婚不需要政府允许的extension MarriageProtocol where Self: Human {    // 誓言承诺    var promise: String {        return "我愿意无论是顺境或逆境,富裕或贫穷,健康或疾病,快乐或忧愁,都将毫无保留地爱ta,对ta忠诚直到永远。无论是健康或疾病。贫穷或富有,无论是年轻漂亮还是容颜老去,我都始终愿意与ta,相亲相爱,相依相伴,相濡以沫,一生一世,不离不弃"    }    // 政府允许    var approveByGovernment: Bool {        return true    }}

剩下的你们遵守婚姻协议就好了

// 人类遵守了婚姻协议:1.履行承诺;2.政府认可extension Human: MarriageProtocol {}let kotlinerPromise = kotliner.promiselet angelPromise = angel.promiselet angelAuthentic = kotliner.approveByGovernment // truelet kotlinerAuthentic = angel.approveByGovernment // true

好了,你们的婚姻都有政府见证,都有对方的誓言。恭喜你kotlin君,你脱单了

Kotlin

Swift君 ,我说不出话了。好兄弟无言表达我的谢意;

Swift

这没什么,我只是分享指导了我的撩妹技巧而已;

内心独白:

TMD,为啥我Swift君指导别人都能成功,自己单身狗当着都是偷偷摸摸的。不做ios了,我要转Android开发;脱单的可能不是考秘籍,而是靠Google爸爸啊

拓展 Extensions 知识点 ( Swift 篇 )

Swuft 扩展 就是为一个已有的类、结构体、枚举类型或者协议类型添加新功能。和OC的category类似
- 拓展语法
- 计算属性
- 构造器
- 方法
- 下标
- 嵌套类型
- 协议

拓展语法: 举几个常见的

class Human: NSObject {    var name = "my_vc"    init(name: String) {        self.name    }    func work() {        print("工作中")    }}extension Human {    func drivingTest() {        print("通过驾照考试")    }}extension Human: UITableViewDelegate, UITableViewDataSource {    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {        return 3;    }    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {        return UITableViewCell()    }    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {        print(name)    }}

计算型属性

// 基础类型extension Double {    var km: Double { return self / 1000.0 }    var m : Double { return self }    var cm: Double { return self * 100.0 }    var mm: Double { return self * 1000.0 }}let distance = Double(7353)distance.km// 结构体(class 类似)struct iOSer {    var name: String}extension iOSer {    var isValid: Bool {        return !name.isEmpty    }    mutating func work() {        name = name + "_coding_dog"    }}// 枚举拓展enum AppleDevice {    case iPhone,iPod,iPad,Mac,iMac}extension AppleDevice {    func whatShouldIBuy(money: Int) -> AppleDevice {        if money < 10000 {            return .iPhone        }        return .Mac    }}

支持构造器

值类型可以直接构造,引用类型需要使用便利构造器

// 参考已有的如上代码:iOSer、Humanextension iOSer {    init(firstName: String, secondName: String) {        self.name = firstName + secondName    }}extension Human {    convenience init(firstName: String, secondName: String) {        self.init(name: firstName + secondName)    }}

方法

静态方法,实例方法,类方法

extension iOSer {    static func bestPrictise() -> String {        return "Swift3.1"    }    func favoriteLanguage() -> String {        return "\(name) loved swift"    }}let bp = iOSer.bestPrictise()let xcodeyang = iOSer(name: "xy")xcodeyang.favoriteLanguage()extension Human {    static func tranlateChinese() -> String {        return "人类"    }    class func tranlateJanpanese() -> String {        return "ピープル"    }    func coding(code:(()->Void)?) {        code?()    }}let chineseName = Human.tranlateChinese()let janpaneseName = Human.tranlateJanpanese()let me = Human(firstName: "yang", secondName: "zhi")me.coding {    print("Hello World")}

嵌套类型

比如结构体里面的枚举等等

extension iOSer {    class Job {        var experience: Int = 0 {            didSet{                salary = experience>3 ? 50:10            }        }        var salary = 0        init(l: Language) {            if l != .oc {                experience = 3                salary = 30            }        }    }    enum Language {        case oc, swift, java, js, c    }    var myFavoriteDSL: Language {        return .js    }    var newJob: Job {        return Job(l: myFavoriteDSL)    }}xcodeyang.newJob.salary

下标

常见数组,字典等等

extension Int {    subscript(digitIndex: Int) -> Int {        var decimalBase = 1        for _ in 0..<digitIndex {            decimalBase *= 10        }        return (self / decimalBase) % 10    }}746381295[0] // 7746381295[5] // 1

扩展 && 协议

详细可以参考后面协议的部分

通过扩展遵循协议

protocol TextRepresentable {    var textualDescription: String { get }}struct Human {    var name: String    var textualDescription: String {        return "这个人叫\(name)"    }}extension Human: TextRepresentable {}let textProtocal: TextRepresentable = Human(name: "john")textProtocal.textualDescription// 或者let human = Human(name: "john")human.textualDescriptionprotocol PrettyTextRepresentable: TextRepresentable {    var prettyTextualDescription: String { get }}extension Human: PrettyTextRepresentable {    var prettyTextualDescription: String {        return name.isEmpty ? "❌: 人名无效" : "✅:这个人叫\(name)"    }}let john = Human(name: "john")let unnamed = Human(name: "")print(john.prettyTextualDescription)print(unnamed.prettyTextualDescription)

通过扩展遵循协议实现协议的默认实现

protocol PlayGame {    var gameName: String {get}    func startGame()    func endGame()}extension PlayGame {    func startGame() {        print("\(self.gameName) 游戏开始了")    }    func endGame() {        print(self.gameName + " 结束了")    }}extension Daniel: PlayGame {    var gameName: String {        return "王者荣耀"    }}human.gameNamehuman.startGame()human.endGame()

为协议扩展添加限制条件

// 娱乐项目protocol Entertainment {    var name: String {get}    func haveFun()}extension Entertainment where Self: Programmer {    var name: String {        return "王者荣耀"    }    func haveFun() {        print("开始玩\(name)啦。。")    }}extension Entertainment where Self: Producter {    // 拓展里只能是计算属性    var name: String {        return "狼人杀"    }    func haveFun() {        print("来一起玩\(name),怎么样")    }}class Programmer: Entertainment {}class Producter: Entertainment {}class Designer: Entertainment {    func haveFun() {        print("自己看看\(name)")    }    var name: String = "动画片"}let prog = Programmer()prog.haveFun()let prod = Producter()prod.haveFun()let desi = Designer()desi.haveFun()

集合的运用

extension Collection where Iterator.Element: Entertainment {    var allNames:String {        return self.reduce("结果:", {            $0 + "\n" + $1.name        })    }}let representableArray = [Designer(),Designer(),Designer()]// 留下待解决问题//let representableArray = [Programmer(),Designer()] as [Entertainment]print(representableArray.allNames)

扩展 Extensions ( Kotlin 篇 )

在Kotlin中,允许对类进行扩展,不需要继承或使用 Decorator 模式,通过一种特殊形式的声明,来实现某一具体功能。扩展函数是静态解析的,并未对原类增添函数或者属性,也就是说对其本身没有丝毫影响。

扩展函数:

在kotlin里,通过扩展函数,我们可以很方便的为一个类添加一个方法。

fun String.lastChar(){    this.get(this.length - 1)}fun String.lastChar() = get(this.length - 1)

上面两种写法其实是一样的。其中,this关键字指代接收者对象(receiver object)(也就是调用扩展函数时, 在点号之前指定的对象实例),有时可以省略。

扩展函数的调用

以上面的函数为例

 "Android".lastChar()

扩展函数的声明格式:

fun receiverType.functionName(params){    body}

其中
- receiverType:表示函数的接收者,也就是函数扩展的对象
- functionName:扩展函数的名称
- params:扩展函数的参数,可以为NULL
- body 函数体

扩展函数是静态解析的

扩展方法是静态解析的,而并不是真正给类添加了这个方法。
调用的扩展函数是由函数调用所在的表达式的类型来决定的,而不是由表达式运行时求值结果决定的。

open class Animal{}class Cat : Animal()object Main {    fun Animal.food() = "food"    fun Cat.food() = "fish"    fun Animal.printFood(anim: Animal){        println(anim.food())    }    @JvmStatic fun main(args: Array<String>) {        Animal().printFood(Cat())    }}

最终的输出是 food ,而不是 fish 。
因为扩展方法是静态解析的,在添加扩展方法的时候类型为Animal,那么即便运行时传入了子类对象,也依旧会执行参数中声明时类型的方法。

成员函数和扩展函数

如果一个类定义有一个成员函数和一个扩展函数,而这两个函数又有相同的接收者类型、相同的名字并且都适用给定的参数,这种情况总是优先调用成员函数。

class Dog : Animal(){    fun printFood(){          println("meat")    }}object Main {    fun Dog.printFood(){        println("bone")    }    @JvmStatic fun main(args: Array<String>) {        val dog: Dog = Dog()        dog.printFood()    }}

这里输出 meat。

扩展属性

val TextView.leftMargin:Intget():Int {     return (layoutParams as ViewGroup.MarginLayoutParams).leftMargin    }set(value) {        (layoutParams as ViewGroup.MarginLayoutParams).leftMargin=value    }

由于扩展没有实际的将成员插入类中,因此对扩展属性来说幕后字段是无效的。所以,对于扩展属性不允许存在初始化器。 扩展属性的行为只能由显式提供的 getters/setters 定义。也就意味着扩展属性只能被声明为val而不能被声明为var.如果强制声明为var,即使进行了初始化,在运行也会报异常错误,提示该属性没有幕后字段。

伴生对象的扩展

如果一个类定义有一个伴生对象 ,你也可以为伴生对象定义扩展函数和属性:

fun Animal.Companion.eat() {    println("eat")}val Animal.Companion.food: Stringget() = "food"@JvmStatic fun main(args: Array<String>) {    println("food:${Animal.food}")    Animal.eat()}

对于伴生对象的扩展属性和方法,只需用类名作为限定符去调用他们就可以了。

扩展的作用域

我们在a包中定义的扩展方法

package afun Cat.food() { …… } 

要在a包之外使用这个扩展,我们需要在调用方导入它:

package bimport a.food // 导入所有名为“food”的扩展                   // 或者import a.*   // 从“a包”导入一切fun usage(cat: Cat) {    cat.food()

最后安利一下:一起聊聊 Kotlin 在 Android、JS、JVM(后端)、Native、DSL各个平台的技术实践与分享,关注此公众号,加入我们
关注此公众号