[Swift]多态类型转换以及不确定类型

本文介绍了Swift中的多态特性,包括如何利用父类引用指向子类实例实现多态,以及如何通过is和as运算符进行类型转换。同时,详细阐述了不确定类型Any和AnyObject的区别,以及如何在不确定类型中使用is和as进行操作。最后,提到了整体转换数组的方法,但需要注意转换的安全性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Swift的多态:

    1) 和其它语言多态描述一致,都是用父类指针或引用(这里的父类是指祖先类)指向子类的实例,然后在子类中覆盖父类的方法,利用该父类引用调用相同的方法而产生不同的行为;

    2) Swift的多态类型转换:和普通的类型转换不一样,普通的类型转换是指一般意义上的强制类型转换,但是强制类型转换不能发生在类型之间,如果使用"类名(转换对象)"则会触发相应类的构造器而不能达到转换类型的效果,因此Swift的类型转换只能发生在可以转换的两个对象之间,对于不能转换的两个对象会在编译阶段或是运行阶段报错;

Swift专门为多态之间的类型转换提供了解决方案,它的机制有严格的规则要求,首先必须要让一个父类的引用指向一个其子类的实例(这个是大前提),然后可以利用is运算符来判断该引用指向的实例是否属于某个子类,同时可以使用as运算符将该引用指向的实例正确地转化成相应的子类类型,这种转换也称为向下转换(即先将子类实例用父类包装,等到使用时再把它彻底扩展成子类);

这就好比有一个动物的序列,序列中有各种各样的动物,起初不知道该序列里每个动物都属于哪一类(比如猫科、鸟类等),但大前提是这些子类(猫科、鸟类、爬行类等都属于动物这个父类的子类),因此起初可以将这整个序列里的元素都当成动物这个基类来看待(is-a的关系,C++中解释继承关系的经典形容),然后逐个取出序列里的元素判断其具体属于哪个子类(是猫科呢还是鸟类等),这是就使用is来判断,如果确实属于猫科,则使用as操作符将一般性的动物类的猫转化成具体的猫科类,这样就可以使用猫所具有但动物所不具有的功能(基类引用只能访问基类的属性以及实现多态,但不能访问子类的新增部分);

    3) 首先看一个多态的例子:

class A {
    func show() {
        println("A")
    }
}

class B: A {
    override func show() {
        println("B")
    }
}

var a = A()
a.show() //  A

a = B()
a.show() // B


2. Swift的多态类型转换:

    1) 即is和as两个操作符的运用;

    2) 还是强调那句,这两个操作符的使用必须严格遵守之前讲过的规则:必须要让一个父类的引用指向一个其子类的实例,然后再对该引用用is判断是否属于某个具体的子类或用as将其转换成某个具体的子类类型;

    3) is的使用:

class A {
    
}

class B: A {
    
}

class C: A {
    
}

var cnt_b = 0, cnt_c = 0

// 都是A的子类因此发生多态,obj的类型为A
let obj = [B(), B(), C(), C(), B()]

for item in obj {
    if item is B { // 注意is的左边的类型必须是右边类型的父类!否则就报错!
        cnt_b++
    }
    else if item is C {
        cnt_c++
    }
}

println("cnt_c = \(cnt_c), cnt_b = \(cnt_b)") // 2 3

var obj_a = A()
// 只能判断一个用父类引用指向的子类实例是否属于某个子类
// 不能用于判断非以上关系的实例是否属于某个类

// println(obj_a is A) // Bad! 必须是父类引用指向子类实例,指向自己也不能判断,会直接报错的!
// println(B() is B) // Bad! 理由同上,不能判断自己是否属于自己的类型
// println(obj_a is String) // Bad!只能用于多态引用

class D: B {
    
}

var d: A = D()
println(d is D) // OK! 多层继承的多态引用也行!
    4) as的使用(接上例代码):

cnt_b = 0
cnt_c = 0
for item in obj {
    if let obj_b = item as? B { // 同样as左侧的类型必须是右侧的父类,同样两侧的类型不能相同
        cnt_b++
    }
    else if let obj_c = item as? C {
        cnt_c++
    }
}
println("cnt_c = \(cnt_c), cnt_b = \(cnt_b)") // 2 3
!使用as时如果能转换则成功,如果不能则会抛出运行时异常,为了不抛出异常在as后加?可以起到和可选链一样的功能,只返回nil而不会那么暴力地抛出异常,同时配合let绑定可以达到一举两得的效果,相当于is判断的同时也同时实现了类型转换!


3. 两种不确定类型——Any和AnyObject:

    1) 注意Swift的不确定类型和Java的Object类不一样,它并不是所有类的基类,可以说它们并不是类,而只是一种不确定类型(类似C语言的void类型);

    2) AnyObject可以接受任何类类型数据,而Any可以接受任意类型的数据(包括AnyObject和基本类型),由于基本类型不是类类型,因此可见这两种不确定类型并不是类类型;

    3) 虽然不是类类型,但是可以使用is和as操作符,比如0 is Int或0 as Double,其实这里的,其实这里的is和as都使用Any进行重载;

    4) 整个数组整体转换(前提是数组中所有元素类型必须一样):形式是AnyOrAnyObjectArray as [SpecificType],这样就可以返回一个转换好的[SpecificType]数组了,但是不能使用可选符号?进行保护!

class A {
    
}

class B {
    
}

var arr: [AnyObject] = [B(), B(), B(), B(), B(), B()]

for item in arr as [B] { // 这里不能写成as?或[B?],否则会报错
    // 数组整体转换必须是强制而不能使用可选?,这可能是Swift不足的地方,因此这里强制要求arr中所有元素类型一致,否则就抛出运行时异常,因为不能用?保护
    println("is B")
}
     5) 一个Any的例子:

class A {
    
}

class B {
    
}

var arr: [Any] = [1, 3.923, A(), "xlkfds", B(), B(), 9 / 12, A()]

for item in arr {
    if let a = item as? A {
        println("A")
    }
    else if let b = item as? B {
        println("B")
    }
//    else if let c = item as? Any { // 不能转换成Any或AnyObject自己本身,编译器从语法上不允许这样做!
//        println("haha")
//    }
} // A B B A


4. 类型转换在switch中的应用:

    1) 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值