5

I want to pass an instance of a parent class to a variable whose type is a subclass of this parent.

There are two classes as follow:

class Parent{
    var aaa:String?
    var bbb:String?
    init(pa:String?,pb:String?){
        self.aaa=pa
        self.bbb=pb
    }
}

class Child:Parent{
    var ccc:String?
}

Now, I use them like this:

let p=Parent(a:"1111",b:"22222")
if let c=p as? Child{
   print(c)
}

But the variable is nil in any cases.

I would like to get the result as follow: c.aaa="1111" c.bbb="222" c.ccc=nil or other

pkamb
  • 33,281
  • 23
  • 160
  • 191
RonnieLee
  • 83
  • 1
  • 6
  • sometimes display: could not cast value of type 'parent' to 'child' – RonnieLee Aug 17 '16 at 10:55
  • 2
    fyi, I suggest you name your classes Parent & Child, as you should not have types starting with a lowercase string – Yasir Aug 17 '16 at 10:59
  • You cannot cast a superclass instance to a subclass type: the superclass contains a subset of properties of the subclass, in which case a given superclass instance does not hold sufficient information to directly convert into a subclass object. See e.g. [the following Q&A](http://stackoverflow.com/questions/4862960/explicit-casting-from-super-class-to-subclass) for details. – dfrib Aug 17 '16 at 11:00
  • In respect to your question, the if let condition would never succeed as **p** is an instance of _parent_, not _child_ so the cast would fail. – Yasir Aug 17 '16 at 11:00
  • but in this way.how can i got the result i need? – RonnieLee Aug 17 '16 at 12:53
  • just like this ? let c.aaa=p.aaa let c.bbb=p.bbb? if i have lots of varies.i need write thousand line? – RonnieLee Aug 17 '16 at 12:55

2 Answers2

5

First of all let's clean up your code

class Parent {
    var a:String?
    var b:String?
    init(a:String?, b:String?) {
        self.a = a
        self.b = b
    }
}

class Child: Parent{
    var c: String?
}

Answer

With this code you create a Parent object and you put it into the parent constant.

let parent = Parent(a:"1111",b:"22222")

Then you try to cast parent as Child. But Parent is NOT a Child. So the cast will fail.

if let child = parent as? Child {
    print(child)
}

Conclusion

class Child: Parent { ...}

you are saying that a Child is also a Parent.

NOT that a Parent is a Child.

Update

If you want to create a Child object using a Parent object (if this makes sense for your business logic) here's the code

class Child: Parent{
    var c: String?

    init(parent:Parent, c:String) {
        super.init(a: parent.a, b: parent.b)
        self.c = c
    }        
}

let parent = Parent(a: "a", b: "b")
let child = Child(parent: parent, c: "c")
child.a // "a"
child.b // "b"
child.c // "c"

has-a instead of is-a

Another possible solution is avoiding subclassing, here's the code

struct Parent {
    var a:String?
    var b:String?
}

struct Child {
    var c: String?
    var parent: Parent
}

Now Child is not a Parent but contains a Parent

You probably should rename them

Now given a parent

let parent = Parent(a: "a", b: "b")

you can easily create a Child

let child = Child(c: "c", parent: parent)

And this is how you use the child

child.parent.a // "a"
child.parent.b // "b"
child.c // "c"
Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
  • but in this way.how can i got the result i need?just like this ? let c.aaa=p.aaa let c.bbb=p.bbb? if i have lots of varies.i need write thousand line? – – RonnieLee Aug 17 '16 at 12:56
  • @RonnieLee: So you want to copy `aaa` and `bbb` from a `Child` object to a `Parent` object? – Luca Angeletti Aug 17 '16 at 12:58
  • no, i want to copy aaa and bbb from a parent object to child – RonnieLee Aug 17 '16 at 12:59
  • @RonnieLee: Sorry, my mistake in my previous comment. Besides manually copying that values you could create an `init` inside `Child` that accept a `Parent` object. I'm going to add the code. – Luca Angeletti Aug 17 '16 at 13:00
  • :)thank you for your help! i think if manually copying when there are lots of varies in this class. it's a so hard work! – RonnieLee Aug 17 '16 at 13:04
  • i see,thanjk you .i try it advence. but this method is also like manually copy.if i have lots of varies what can i do ? – RonnieLee Aug 17 '16 at 13:07
  • super.init(a: parent.a, b: parent.b, c:parent.c, d:parent.d...etc....)? – RonnieLee Aug 17 '16 at 13:09
  • @RonnieLee: The problem is that if you have a `Parent` object you __can't__ cast it to `Child`. Casting doesn't work this way. You explicitly need to create the `Child` object and populate it's properties. – Luca Angeletti Aug 17 '16 at 13:10
  • especially ,the parent class have same name variable. i think it should have a good way to do this – RonnieLee Aug 17 '16 at 13:11
  • @RonnieLee: I added another way of doing it, please take a look. – Luca Angeletti Aug 17 '16 at 13:21
  • right!!!!!!!!!!!!!can i use it like class? i mean class Parent{...} class child{ var c:string, var parent:Parent} – RonnieLee Aug 17 '16 at 13:27
  • @RonnieLee: Yes you can use class too. In this case you'll need to manually define the initializers though. – Luca Angeletti Aug 17 '16 at 13:28
1

The comments in response to your question will tell you why your conditional cast will always fail. To print out the value of c as an instance of Child, the following should work:

class Parent {
    var aaa:String?
    var bbb:String?

    init(aaa: String?, bbb: String?) {
        self.aaa = aaa
        self.bbb = bbb
    }
}

class Child : Parent {
    var ccc:String?
}

var child = Child(aaa: "", bbb: "")
print(child)
Yasir
  • 2,312
  • 4
  • 23
  • 35
  • Assuming the naming _aaa_, _bbb_ and _ccc_ are just examples and not your normal naming strategy! :) – Yasir Aug 17 '16 at 11:06
  • thanks for your help .this just example .but this result is not i need .i want to copy aaa and bbb from a parent object to a child object – RonnieLee Aug 17 '16 at 13:05