Как вернуть объекты подкласса из статического метода базового класса?

#swift

Вопрос:

В Swift я хочу, чтобы мой статический метод базового класса возвращал объекты подкласса, когда статический метод вызывается из подкласса.

При возврате одного объекта подкласса я могу сделать это возможным с помощью init(). Но при возврате нескольких объектов подкласса init() нельзя использовать.

И я хочу не только просто возвращать объекты подкласса из родительского статического метода, но и реализовать некоторую логику, отличную от создания экземпляра в родительском статическом методе, и заставить статический метод каждого подкласса наследовать поведение родительского статического метода.

У меня есть 3 подкласса. Итак, я не хочу писать один и тот же код в статических методах 3 подклассов. Как я должен поступить?

Если возможно, я также хочу использовать статический метод вместо init() для возврата одного объекта подкласса.

 class Base {
    
    func f() {
        print("base class")
    }
    
    // this does not works. it creates a base class object.
    static func createSubclassObject() -> Base {
        return Base()
    }
    
    // this works. it creates a subclass object.
    init() {
    }
    
    // this does not work. base class objects are created.
    static func createSubclassObjects(count: Int) -> [Base] {
        var objects = [Base]()
        for _ in 0 ..< count {
            objects.append(Base())
        }
        return objects
    }

    /* probably I need something like this. but this code generates a compile error
    static func createSubclassObjects(count: Int) -> [Self] {
        var objects = [Self]()
        for _ in 0 ..< count {
            objects.append(Self())
        }
        return objects
    }
    */

    // generic also does not work. this returns a base class object.
    static func createSubclassObjectByGeneric<T: Base>() -> T {
        return T()
    }

}
 
 class Sub: Base {
    override func f() {
        print("sub class")
    }
}
 
 print(Sub().f())
// sub class・
 
 print(Sub.createSubclassObject().f())
// base class
 
 Sub.createSubclassObjects(count: 2).forEach {
    print($0.f())
}
// base class
// base class
 
 print(Sub.createSubclassObjectByGeneric().f())
// base class
 

Ответ №1:

Тебе нужно вернуться Self , а не Base .

 static func createSubclassObject() -> Self {
  .init()
}

required init() { }
 

Кроме того, не используйте цикл for. Существует инициализатор массива, предназначенный для того, что вы делаете.

 static func createSubclassObjects(count: Int) -> [Base] {
  .init(repeating: createSubclassObject(), count: count)
}
 

Комментарии:

1. спасибо вам за отличный ответ! Это прекрасно работает. Ваш ответ-это то, что я так долго искал. И я обнаружил Array(repeating: Self.init(), count: count) , что также возвращает объекты подкласса. Но Array(repeating: .init(), count: count) возвращает объекты базового класса.

Ответ №2:

Работает следующий код. Но я думаю, что должны быть лучшие решения, потому что я не хочу определять class func sub() в родительском классе и переопределять class func sub() в каждом подклассе.

ПРАВКА: Смотрите ответ Джесси, который является лучшим решением.

 class Base {
    
    func f() {
        print("base class")
    }
    
    static func createSubclassObjects(count: Int) -> [Base] {
        var objects = [Base]()
        for _ in 0 ..< count {
            //objects.append(Base())
            objects.append(Self.sub())
        }
        return objects
    }
    
    class func sub() -> Base {
        Base()
        // or use fatalError() if you don't need to call createSubclassObjects(count: Int) from the base class
    }
    
}
 
 class Sub1: Base {
    
    override func f() {
        print("sub1 class")
    }
    
    override class func sub() -> Base {
        Sub1()
    }
    
}
 
 class Sub2: Base {
    
    override func f() {
        print("sub2 class")
    }
    
    override class func sub() -> Base {
        Sub2()
    }
    
}
 
 Base.createSubclassObjects(count: 2).forEach {
    print($0.f())
}
// base class
// base class


Sub1.createSubclassObjects(count: 2).forEach {
    print($0.f())
}
// sub1 class
// sub1 class


Sub2.createSubclassObjects(count: 2).forEach {
    print($0.f())
}
// sub2 class
// sub2 class