0

I am trying to create a couple of objects which are dependent one to each other and they mush have a method to downcast directly the concrete class of the other object. Something like this:

protocol aProt
{
    var bVar:bProt! { get set }
}

protocol bProt
{
    var aVar:aProt!  { get set }
}

class a: aProt
{
    var bVar: bProt!
    func bConcrete() -> b {
        return bVar as! b
    }
}

class b: bProt
{
    var aVar: aProt!
    func aConcrete() -> a {
        return aVar as! a
}

Now, the problem is that I want this behavior (func aConcrete(),func bConcrete()) to be inherited by the subclasses of a and b. Then I thought the perfect way of doing this was using generics, but... There's no way of doing this.

class a: aProt { var bVar: bProt! func bConcrete() -> T { return bVar as! T } }

class b: bProt
{
    var aVar: aProt!
    func aConcrete<T>() -> T {
        return aVar as! T
}

You can do it but when you have to use it you must downcast the variable anyway, so there is no way of doing it in a clean manner:

let aObject = a()
let bSubclassObject = a.bConcrete() // The compiler complains it cannot infer the class of T
let bSubclassObject = a.bConcrete() as! bSubclass // this works, but this is exactly which I wanted to avoid... :(
Pablo Romeu
  • 2,113
  • 1
  • 13
  • 15
  • If you are down casting like this then something is broken. You should never have to down cast from a protocol to a concrete implementation. That's what protocols are for. – Fogmeister Dec 29 '16 at 10:15
  • what is the compiler supposed to do?? You say "cast this object bVar" and then you continue "... to type T, but I am not gonna tell you what T is". – luk2302 Dec 29 '16 at 10:15
  • @Fogmeister I'm trying to create a generic reference from one to each other (a and b) but in a concrete implementation they will always be subclassed so I need a downcast the result – Pablo Romeu Dec 30 '16 at 10:30
  • @luk2302 the point is I should not have to tell you because T is a concrete var you have stored inside your property (bVar). It is not something external. – Pablo Romeu Dec 30 '16 at 11:21
  • 1
    That is not how generics work – luk2302 Dec 30 '16 at 11:31
  • @luk2302 Any ideas of how could I achieve this behavior? – Pablo Romeu Dec 30 '16 at 11:38

1 Answers1

1

Define the generic function and add where to T:

protocol aProt {
    var bVar: bProt! { get set }
}

protocol bProt {
    var aVar:aProt!  { get set }
}

class a: aProt {
    var bVar: bProt!
    func bConcrete<T: b>(_ type: T.Type) -> T? {
        return bVar as? T
    }
}

class b: bProt {
    var aVar: aProt!
    func aConcrete<T: a>(_ type: T.Type) -> T? {
        return aVar as? T
    }
}

class a1: a { }
class b1: b {
    var fullName: String = "new object"
}

let aObj = a()
aObj.bVar = b1()
let bObj = aObj.bConcrete(b1.self)
bObj?.fullName

According to your requirement, calls bConcrete(b1.self) might still not good enough, but at least you need to know what type of data you are expecting to return.

bubuxu
  • 2,187
  • 18
  • 23
  • This is almost what I wanted to achieve, but when I try to access a new method/property defined on b1 the compiler complains :( – Pablo Romeu Dec 30 '16 at 11:19
  • When you call bConcrete(), do you know what's the return type will be? If the function changed to `func bConcrete(_ type: T.Type) -> T?` would it be helpful? – bubuxu Dec 30 '16 at 11:30
  • The point is I wanted not to tell the compiler the type. If I am telling the tipe then there's no point of creating the function, because downcasting using "as" is more straightforward... (e.g. it is easier to write `aObjc.bVar as b1`) – Pablo Romeu Dec 30 '16 at 11:40
  • I edited the origin answer and make it more easier to define the return type without using `as?` checking. – bubuxu Dec 30 '16 at 11:55
  • I accept your answer as it is the most close solution to the problem, but anyway `let bObj = aObj.b as b1` is more straightforward than `let bObj = aObj.bConcrete(b1.self)` so I will stick to the first solution – Pablo Romeu Dec 30 '16 at 13:00