3

I hope to get a specific instance when I use my static method. For example:

class Food: NSObject {
    var name: String

    class func initFruit() -> Food? {
        let fruitName = NSStringFromClass(self).components(separatedBy: ".").last! as String

        if "Apple" == fruitName {
            return Apple(name: fruitName)
        } else if "Orange" == fruitName {
            return Orange(name: fruitName)
        }
        return nil
    }

    init(name: String) {
        self.name = name
    }

}

class Apple: Food {
}
class Orange: Food {
}

When I create an Apple instance with the method:

let apple = Apple.initFruit() as? Apple

How can I get the specific instance apple rather than use as? Apple?. I wonder how to modify the method:

 static func initFruit() -> Food?
Cristik
  • 30,989
  • 25
  • 91
  • 127
dowZhang
  • 470
  • 1
  • 3
  • 18
  • this can be easily achieve using protocol and generic instead of inheritance. Plus, Apple's guide for Swift has shifted to Protocol Oriented, and imo that's how you should do it. – Eddie Feb 14 '19 at 04:04
  • 1
    I agree with @Eddie, however, I'd like to offer some advice. The whole point of generics or inheritance is so you don't HAVE to know the type, i.e. `Apple` or `Orange`. So when you initialize you might have, `if(Apple){}, else if(Orange){}...` however, after that you could just do `Food.isEaten` or `Food.jumpIntoCrockPot()` instead of having to have a `Apple.jumpIntoCrockPot()` and a `Orange.jumpIntoCrockPot()`. Thus, the notion that you want to create a static instance of `Food.init() as? Apple` removes the point of `Food.init()` because you have now type casted it into `Apple`. – impression7vx Feb 14 '19 at 04:10
  • This is only an example.I wonder whether or not it would get an specific instance from superclass static method. – dowZhang Feb 14 '19 at 04:19
  • 1
    I don't think you can with `static`, but if you change to `class func` and override them in your subclass then you can return with exact class. Superclass should NOT know about subclass so you cant do this. Prefer use factory or generic type – Tj3n Feb 14 '19 at 04:57
  • Why would you call `initFruit`? Why not directly instantiating `Apple`? The static method provides almost no extra functionality. – Cristik Feb 14 '19 at 06:34
  • `class func initFruit() -> Food?` here class function return optional value and so you have to cast as `as? Apple`. Try with `self` or `Food`. – Ankit Jayaswal Feb 14 '19 at 06:51

3 Answers3

4

There are a couple of problems with your design, let me try and enumerate them:

  • base classes should not be aware of their subclasses, it's bad practice and it's not scalable, as adding a new subclass would require maintaining the base class method
  • the static method is not needed at all, at least in the shape it's written in the question, you could simply directly call the initializers for the subclasses

Leaving all those aside, you can use Self as return type for the static method, this will allow dynamic results.

class Food: NSObject {
    var name: String

    class func initFruit() -> Self {
        let fruitName = NSStringFromClass(self).components(separatedBy: ".").last! as String

        return self.init(name: fruitName)
    }

    required init(name: String) {
        self.name = name
    }

}

class Apple: Food {
}
class Orange: Food {
}

let apple = Apple.initFruit() // is an Apple, no cast needed
Cristik
  • 30,989
  • 25
  • 91
  • 127
2

i think it's not good idea because Food is parent class and Apple inherits Food. Apple may know it's parent class cause is extends Food but Food does not.

So, if you want to create instance by some string or some variable. I would like to recommend you to adopt "Factory pattern"

reference here: https://medium.com/swift-programming/design-patterns-creational-patterns-factory-pattern-in-swift-d049af54235b

0

Inspired by the question Generics in Swift - "Generic parameter 'T' could not be inferred I find another way to resolve this question. I add a method to infer the specific type.

func ascertainFruitType<T>() -> T {
    return self as! T // as! is dangerous
}

Then the method initFruit is changed on below:

class func initFruit() -> Self {
    let fruitName = NSStringFromClass(self).components(separatedBy: ".").last! as String

    if "Apple" == fruitName {
        return Apple(name: fruitName).ascertainFruitType()
    } else {
        return Orange(name: fruitName).ascertainFruitType()
    }

}
dowZhang
  • 470
  • 1
  • 3
  • 18