【问题标题】:How to write a return type of a method as a class that conforms to a protocol + swift如何将方法的返回类型编写为符合协议+ swift的类
【发布时间】:2021-04-27 10:03:46
【问题描述】:

我正在做一个项目,我需要在 iOS 中使用 swift 复制使用 android 开发的功能。

在android应用程序中,有一个地方提到了抽象类Device中方法的返回类型为,

abstract fun getDT(): Class<out DeviceType>

其中 DeviceType 本身就是另一个抽象类。所以我听android开发者说,在这个方法的实际实现中,它会返回一个继承DeviceType的类,如下所示,

override fun getDT(): Class<out DeviceType> {
    return type1DeviceType::class.java
}

其中type1DeviceType实际上继承了DeviceType抽象类如下

public class type1DeviceType extends DeviceType {

所以在我们的 iOS 术语中,抽象类的等价物是协议。

所以在 iOS 中,我编写了一个协议来代替 Android 中的抽象类。而对于其中抽象函数的返回类型,我需要提到返回类型是符合DeviceType协议的东西。知道如何实现这一目标吗?

我在swift中尝试了以下代码。

 public func getDT() -> DeviceTypeProtocol.Type {
    return type1DeviceType as! DeviceTypeProtocol.Type
}

但是在运行时,我得到了错误,

Swift 运行时失败:类型转换失败

【问题讨论】:

  • 你能发布你的代码来定义type1DeviceType
  • public class type1DeviceType: DeviceTypeProtocol { //所有类相关代码 }
  • 您可以将返回值键入为 DeviceTypeProtocol,而不是 DeviceTypeProtocol.Type。你会返回一个实际的对象,而不是一个类。

标签: ios swift kotlin protocols abstract


【解决方案1】:

你的意思是这样的吗?

protocol DeviceType {
    func getDeviceType() -> DeviceType.Type
}

extension DeviceType {
    func getDeviceType() -> DeviceType.Type { Self.self }
}



class AudioDevice: DeviceType {
    func getDeviceType() -> DeviceType.Type { AudioDevice.self }
}

class Microphone: AudioDevice {
}

class Speaker: AudioDevice {
    override func getDeviceType() -> DeviceType.Type { Speaker.self }
}



class VideoDevice: DeviceType {}

class Camera: VideoDevice {}

class Monitor: VideoDevice {
    func getDeviceType() -> DeviceType.Type { VideoDevice.self }
}


func test() {
    print(AudioDevice().getDeviceType()) // prints AudioDevice
    print(Microphone().getDeviceType()) // prints AudioDevice
    print(Speaker().getDeviceType()) // prints Speaker
    print(VideoDevice().getDeviceType()) // prints VideoDevice
    print(Camera().getDeviceType()) // prints Camera
    print(Monitor().getDeviceType()) // prints VideoDevice
}

DeviceType 定义了一个协议,该协议能够返回getDeviceType 的类型,该类型也是DeviceType 的类型。

您所描述的内容不需要扩展协议,但我想以任何一种方式进行演示。用于VideoDevice

所以AudioDevice 继承了协议并明确定义了获取设备类型的方法。因为它返回AudioDevice 的类型,所以它就是打印出来的。 Microphone 继承自 AudioDevice(不是 DeviceType)并且不会覆盖该方法,因此它也返回 AudioDevice。而Speaker 也继承自AudioDevice,但确实覆盖了该方法,因此返回Speaker

VideoDevice 更有趣。它继承了协议,但没有明确定义所需的方法。因此,它使用具有Self.self 的有趣语法的扩展名。如果这更有意义的话,它基本上只是意味着“返回动态自我的静态类型”......这只是可能的,因为定义了协议的扩展。删除扩展会产生一个编译时错误,让您知道您确实需要定义该方法。现在因为很好地定义了扩展,VideoDevice 已经打印出来了。 Camera 也是如此,它继承自 VideoDevice(而不是 DeviceType)。然后Monitor 再次覆盖该方法并打印出VideoDevice 而不是Monitor

当然,您可以将设备类别(在本例中为视频和音频)定义为继承设备类型的协议。您还可以对这些协议进行扩展。看看这个例子:

protocol DeviceType {
    func getDeviceType() -> DeviceType.Type
}


protocol AudioDevice: DeviceType { }

class Microphone: AudioDevice {
    func getDeviceType() -> DeviceType.Type { Microphone.self }
}

class Speaker: AudioDevice {
    func getDeviceType() -> DeviceType.Type { Speaker.self }
}


protocol VideoDevice: DeviceType { }

extension VideoDevice {
    func getDeviceType() -> DeviceType.Type { Self.self }
}

class Camera: VideoDevice {
}
class Monitor: VideoDevice {
    func getDeviceType() -> DeviceType.Type { Camera.self }
}


func test() {
    print(Microphone().getDeviceType()) // prints Microphone
    print(Speaker().getDeviceType()) // prints Speaker
    print(Camera().getDeviceType()) // prints Camera
    print(Monitor().getDeviceType()) // prints Camera
}

【讨论】:

  • 真的不需要.getDeviceType()DeviceType.Type 对我来说真的很奇怪。一个类型不需要持有一个类型,它一个类型。
【解决方案2】:

Swift 协议与 Kotlin 抽象类不同。更接近的比较是 Kotlin interface 和 Swift protocol

我对您的要求有点困惑。但是,根据我在这里看到的情况,Protocol Oriented Programming 似乎是一个很好的案例,它可以减少对多个嵌套抽象类或子类的需求。

我也有点困惑,为什么您需要获取 DeviceType.... 的 DeviceType 是 DeviceType 本身而不是 DeviceType

在我看来,这样的事情似乎要简单得多:

科特林

Kotlin playground

interface Device {
    fun doSomething()
}

interface MobileDevice: Device {
    fun onTap()
}

interface DesktopDevice: Device {
    fun onClick()
}

interface WearableDevice: Device {
    fun onButtonPush()
}

class AndroidPhone: MobileDevice {
    override fun doSomething() {
        println("I'm an Android phone.")
    }
    
    override fun onTap() {
        println("Tap the screen.")
    }
}

class MacDesktop: DesktopDevice {
    override fun doSomething() {
        println("I'm a Mac desktop.")
    }
    
    override fun onClick() {
        println("Click the magic mouse.")
    }
}

class SmartNecklace: WearableDevice {
    override fun doSomething() {
        println("I'm a smart necklace.")
    }
    
    override fun onButtonPush() {
        println("Help! I've fallen and I can't get up!")
    }
}

可以这样使用:

fun exampleFunction() {
    val mobile = AndroidPhone()
    val desktop = MacDesktop()
    val wearable = SmartNecklace()
    mobile.doSomething()
    desktop.doSomething()
    wearable.doSomething()
    
    val devices = listOf(mobile, desktop, wearable)
    
    devices.forEach { device ->
        when (device) {
            is MobileDevice -> device.onTap()
            is DesktopDevice -> device.onClick()
            is WearableDevice -> device.onButtonPush()
            else -> println("Unknown Type.")
        }
    }
}

斯威夫特

在您的 Swift 版本中,您还可以添加一些 default behaviors to the protocols(请参阅下面的协议扩展示例)。

protocol Device {
    func doSomething()
}

protocol MobileDevice: Device {
    func onTap()
}

protocol DesktopDevice: Device {
    func onClick()
}

protocol WearableDevice: Device {
    func onButtonPush()
}

extension Device {
    func doSomething() {
        print("Doing default thing.")
    }
}

extension WearableDevice {
    func onButtonPush() {
        print("Help! I've defaulted and I can't get up!")
    }
}

class AndroidPhone: MobileDevice {
    func onTap() {
        print("Tap the screen.")
    }
}

class MacDesktop: DesktopDevice {
    func doSomething() {
        print("I'm a Mac desktop.")
    }

    func onClick() {
        print("Click the magic mouse.")
    }
}

class SmartNecklace: WearableDevice {
    func doSomething() {
        print("I'm a smart necklace.")
    }
}

可以这样使用:

func exampleFunction() {
    let mobile = AndroidPhone()
    let desktop = MacDesktop()
    let wearable = SmartNecklace()

    mobile.doSomething()
    desktop.doSomething()
    wearable.doSomething()

    let devices: Array<Device> = [mobile, desktop, wearable]

    devices.forEach { device in
        switch (device) {
        case let device as MobileDevice:
            device.onTap()
        case let device as DesktopDevice:
            device.onClick()
        case let device as WearableDevice:
            device.onButtonPush()
        default:
            print("Uknown type")
        }
    }
}

输出

Doing default thing.
I'm a Mac desktop.
I'm a smart necklace.
Tap the screen.
Click the magic mouse.
Help! I've defaulted and I can't get up!

如果你做了这样的事情,你就已经知道对象的性质(如 switch/when 块所示)。您不需要获取设备类型的方法。你会得到它。

如果我在您的问题中遗漏了什么,请告诉我。

至于你的错误:

Swift runtime failure: type cast failed

如果演员阵容失败,我猜type1DeviceTypeDeviceTypeProtocol,而不是DeviceTypeProtocol.Type

public func getDT() -> DeviceTypeProtocol.Type {
    return type1DeviceType as! DeviceTypeProtocol
}

检查当您尝试输入或使用 type1DeviceType 时显示的类型,或查看其快速帮助以查看类型。当你到达那个点时,你认为实际的类型是什么?

我还想问,以与 Android 版本完全相同的方式执行此操作有多重要?我总是建议正确规划跨平台,以使事情尽可能相似。它总是有帮助的,尤其是在作为一个团队一起调试和构建事物时。

从上面的代码可以看出,它几乎是准确的。这也是我做事的方式。但不能以完成工作为代价。

【讨论】:

  • 无需投return type1DeviceType as! DeviceTypeProtocol,只需return type1DeviceType.self
  • @Fx.,正确。那 sn-p 不是“建议的”代码,它是原始问题中的 sn-p。我的主要观点是,他们一开始就尝试使用协议的方式似乎有些不对劲——他们根本不需要“DeviceTypeProtocol.Type”——只要使用 POP,你就会天生就知道类型。
【解决方案3】:
class type1DeviceType: DeviceTypeProtocol {
  public func getDT() -> DeviceTypeProtocol.Type {
    // return type1DeviceType as! DeviceTypeProtocol.Type
    return type1DeviceType.self
  }
}

【讨论】:

  • 添加更多细节来解释你的答案
  • 对不起,只是想强调没有必要投...我在正确答案上添加了评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-23
  • 2015-01-21
  • 1970-01-01
相关资源
最近更新 更多