【问题标题】:Swift: Assigning an object of type sub class to a variable of type base class with generics produces errorSwift:使用泛型将子类类型的对象分配给基类类型的变量会产生错误
【发布时间】:2019-10-19 22:29:22
【问题描述】:

我想分配一个 子类 (TapWhatYouHearViewController),它继承自它的父类 (BaseLectureViewController ) 使用继承的类型 (TapWhatYouHear) 将泛型实现到父类 (BaseLectureViewController 类型的变量) 具有 基类 (Game) 类型的泛型。

Swift 产生以下错误:

Swift:: 错误:无法将类型“TapWhatYouHearViewController”的值分配给类型“BaseLectureViewController?”

我读过,一个解决方案,例如在 Java 中将使用通配符泛型,但这在 Swift 中是不可能的。 还有其他解决方案吗?


这是我的设置:

/** GAME TYPES **/

enum GameType {
    case TapWhatYouHear,
    case ChooseTranslation
}

class Game {
    var title: String?
    var type: GameType?
}

class TapWhatYouHear : Game {
    var audio: String?
    var type: GameType = GameType.TapWhatYouHear
}

class ChooseTranslation : Game {
    var video: String?
    var type: GameType = GameType.ChooseTranslation
}

/** VIEW CONTROLLERS **/

class BaseLectureViewController<T: Game> {
   var game: T?
}

class TapWhatYouHearViewController : BaseLectureViewController<TapWhatYouHear> {

}

class ChooseTranslationViewController : BaseLectureViewController<ChooseTranslation> {

}

我想做以下事情:

var game: Game = TapWhatYouHear() // <-- Depending on the user interaction, this can be any sub class of "Game"
var viewController: BaseLectureViewController<Game>

switch game.type {
    case .ChooseTranslation:
        viewController = TapWhatYouHearViewController() // <--Swift:: Error: cannot assign value of type 'TapWhatYouHearViewController' to type 'BaseLectureViewController<Game>?'
    case .TapWhatYouHear:
        viewController = ChooseTranslationViewController()
}

viewController.game = TapWhatYouHear()

我要做的是在运行时确定BaseLectureViewController 的哪个子类应该分配给viewController 变量。为了节省代码,我想将Game 类型的游戏分配给BaseLectureViewControllergame 属性。

【问题讨论】:

  • 你有必要使用泛型吗?你想做什么,也许有更简单的方法?
  • 对于ChooseTranslationViewController 类,您指定game 属性应为ChooseTranslation,那么为什么稍后要将TapWhatYouHear 分配给game 属性?但是,如果您只知道ChooseTranslationViewControllerGame 类的title 属性,您可以将属性实现为var game: Game,而不是使用泛型
  • @mag_zbc @Saik0s :我更新了我的问题。请看底部的代码和解释。 @Saik0s:我的问题不是关于Game 的具体类型,而是关于将特定的视图控制器分配给父类类型的变量。

标签: ios swift generics inheritance


【解决方案1】:

将变量声明改为

var viewController: BaseLectureViewController<TapWhatYouHear>

或者跳过泛型并将你的超类声明为

class BaseLectureViewController {
    var game: Game?
}

【讨论】:

  • 我更新了“我想做以下”下的代码。 var viewController: BaseLectureViewConroller&lt;Game&gt; 部分是必需的,因为我将在接下来的代码行中确定要分配给 viewController 变量的子类类型。
  • 我认为你需要像上面一样声明它,除非你想在每次需要访问子类特定的属性或函数时强制转换 game 属性
  • 这对我来说没问题,因为我不需要访问 game 属性的任何特定属性。我要做的就是为其父类分配一个子类并分配变量game
  • 查看@mag_zbc 的评论,我也想问同样的问题
【解决方案2】:

如果您只想为game 属性分配Game 的特定子类的实例,那么您可以使用这样的东西,省略泛型

class Game {
    var title: String?

    required init() {}
}

class TapWhatYouHear : Game {
    var audio: String?
}

class ChooseTranslation : Game {
    var video: String?
}

/** VIEW CONTROLLERS **/

class BaseLectureViewController : UIViewController {
    var game: Game

    init(gameType: Game.Type)
    {
        game = gameType.init()
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class TapWhatYouHearViewController : BaseLectureViewController{
    init()
    {
        super.init(gameType: TapWhatYouHear.self)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class ChooseTranslationViewController : BaseLectureViewController{
    init()
    {
        super.init(gameType: ChooseTranslation.self)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

用法

var viewController : BaseLectureViewController
viewController = TapWhatYouHearViewController()

【讨论】:

  • 感谢您的回答。这似乎是合理的。如果BaseLectureViewController 继承自UIViewControllerBaseLectureViewController 的子类的init() 函数将如何变化?
【解决方案3】:

您想到的通用方法是不可能的。我的建议是使用一个简单的继承解决方案,您可以在具体的 ViewController 中覆盖 getGame()(即 TapWhatYouHearViewController)并将游戏转换为您的特定类型。

import UIKit

enum GameType {
    case TapWhatYouHear
    case ChooseTranslation
}

class Game {
    var title: String?
    var type: GameType?
}

class TapWhatYouHear : Game {
    var audio: String?

    override init() {
        super.init()
        type = GameType.TapWhatYouHear
    }
}

class ChooseTranslation : Game {
    var video: String?

    override init() {
        super.init()
        type = GameType.ChooseTranslation
    }
}


/** VIEW CONTROLLERS **/



protocol Lecture {
    var game: Game? { get set }
}

class BaseLectureViewController: UIViewController, Lecture {

    var game: Game?
    func getGame() -> Game? {
        fatalError("Must be implemented in subclass")
    }
}

class TapWhatYouHearViewController : BaseLectureViewController {
    override func getGame() -> TapWhatYouHear? {
        return game as? TapWhatYouHear
    }
}

class ChooseTranslationViewController : BaseLectureViewController {
    override func getGame() -> ChooseTranslation? {
        return game as? ChooseTranslation
    }
}

var game: Game = TapWhatYouHear() // <-- Depending on the user interaction, this can be any sub class of "Game"
var viewController: BaseLectureViewController?

if let gameType = game.type {
    switch gameType {
    case GameType.ChooseTranslation:
        viewController = TapWhatYouHearViewController() // <--Swift:: Error: cannot assign value of type 'TapWhatYouHearViewController' to type 'BaseLectureViewController<Game>?'
    case GameType.TapWhatYouHear:
        viewController = ChooseTranslationViewController()
    }

    viewController?.game = TapWhatYouHear()
}



【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-12
    • 2020-04-21
    • 1970-01-01
    • 2020-05-22
    • 1970-01-01
    相关资源
    最近更新 更多