Swift: Присвоение объекта подкласса типа переменной базового класса с помощью дженериков приводит к ошибке - PullRequest
3 голосов
/ 04 июня 2019

Я хочу назначить подкласс ( TapWhatYouHearViewController ), который наследуется от его родительского класса ( BaseLectureViewController ) с реализацией универсальный, используя унаследованный тип ( TapWhatYouHear ), для переменной типа родительский класс ( BaseLectureViewController ) с универсальным типом Базовый класс ( Игра ).

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 свойству game BaseLectureViewController.

.

Ответы [ 3 ]

2 голосов
/ 04 июня 2019

Общий подход, который вы имеете в виду, невозможен.Я предлагаю пойти с простым решением наследования, где вы переопределяете getGame() в вашем конкретном ViewController (то есть 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()
}



1 голос
/ 04 июня 2019

Если все, что вы хотите сделать, это назначить свойство 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()
1 голос
/ 04 июня 2019

Измените объявление переменной на

var viewController: BaseLectureViewController<TapWhatYouHear>

Или пропустите обобщенные значения и объявите свой суперкласс как

class BaseLectureViewController {
    var game: Game?
}
...