Apakah ada cara untuk membuat kelas abstrak dalam Bahasa Swift, atau apakah ini keterbatasan seperti Objective-C? Saya ingin membuat kelas abstrak yang sebanding dengan apa yang didefinisikan Java sebagai kelas abstrak.
Apakah ada cara untuk membuat kelas abstrak dalam Bahasa Swift, atau apakah ini keterbatasan seperti Objective-C? Saya ingin membuat kelas abstrak yang sebanding dengan apa yang didefinisikan Java sebagai kelas abstrak.
Jawaban:
Tidak ada kelas abstrak di Swift (seperti Objective-C). Taruhan terbaik Anda adalah menggunakan Protokol , yang seperti Java Interface.
Dengan Swift 2.0, Anda kemudian dapat menambahkan implementasi metode dan implementasi properti yang dihitung menggunakan ekstensi protokol. Satu-satunya batasan Anda adalah Anda tidak dapat memberikan variabel atau konstanta anggota dan tidak ada pengiriman dinamis .
Contoh dari teknik ini adalah:
protocol Employee {
var annualSalary: Int {get}
}
extension Employee {
var biweeklySalary: Int {
return self.annualSalary / 26
}
func logSalary() {
print("$\(self.annualSalary) per year or $\(self.biweeklySalary) biweekly")
}
}
struct SoftwareEngineer: Employee {
var annualSalary: Int
func logSalary() {
print("overridden")
}
}
let sarah = SoftwareEngineer(annualSalary: 100000)
sarah.logSalary() // prints: overridden
(sarah as Employee).logSalary() // prints: $100000 per year or $3846 biweekly
Perhatikan bahwa ini menyediakan "kelas abstrak" seperti fitur bahkan untuk struct, tetapi kelas juga dapat mengimplementasikan protokol yang sama.
Juga perhatikan bahwa setiap kelas atau struct yang mengimplementasikan protokol Karyawan harus menyatakan properti AnnualSalary lagi.
Yang terpenting, perhatikan bahwa tidak ada pengiriman dinamis . Ketika logSalary
dipanggil pada instance yang disimpan sebagai SoftwareEngineer
panggilan metode versi ditimpa. Ketika logSalary
dipanggil pada instance setelah di-cast ke Employee
, ia memanggil implementasi asli (itu tidak secara dinamis mengirimkan ke versi yang diganti meskipun instance sebenarnya a Software Engineer
.
Untuk informasi lebih lanjut, lihat video WWDC yang luar biasa tentang fitur itu: Membangun Aplikasi yang Lebih Baik dengan Jenis Nilai di Swift
protocol Animal { var property : Int { get set } }
. Anda juga dapat meninggalkan set jika Anda tidak ingin properti memiliki setter
func logSalary()
ke deklarasi protokol Karyawan, contoh mencetak overridden
untuk kedua panggilan logSalary()
. Ini ada di Swift 3.1. Dengan demikian Anda mendapatkan manfaat polimorfisme. Metode yang benar disebut dalam kedua kasus.
Perhatikan bahwa jawaban ini ditargetkan pada Swift 2.0 ke atas
Anda dapat mencapai perilaku yang sama dengan protokol dan ekstensi protokol.
Pertama, Anda menulis protokol yang berfungsi sebagai antarmuka untuk semua metode yang harus diimplementasikan dalam semua jenis yang sesuai dengannya.
protocol Drivable {
var speed: Float { get set }
}
Kemudian Anda bisa menambahkan perilaku default ke semua jenis yang sesuai dengannya
extension Drivable {
func accelerate(by: Float) {
speed += by
}
}
Anda sekarang dapat membuat tipe baru dengan mengimplementasikan Drivable
.
struct Car: Drivable {
var speed: Float = 0.0
init() {}
}
let c = Car()
c.accelerate(10)
Jadi pada dasarnya Anda mendapatkan:
Drivable
implementasispeed
Drivable
( accelerate
)Drivable
dijamin tidak akan dipakai karena itu hanya protokolModel ini sebenarnya berperilaku jauh lebih seperti ciri-ciri, yang berarti Anda dapat menyesuaikan diri dengan beberapa protokol dan mengambil implementasi default dari salah satu dari mereka, sedangkan dengan superclass abstrak Anda terbatas pada hirarki kelas yang sederhana.
UICollectionViewDatasource
,. Saya ingin menghapus semua boilerplate dan merangkumnya dalam protokol / ekstensi terpisah dan kemudian digunakan kembali oleh beberapa kelas. Faktanya, pola templat akan sempurna di sini, tapi ...
Saya pikir ini adalah yang terdekat dengan Java abstract
atau C # abstract
:
class AbstractClass {
private init() {
}
}
Perhatikan bahwa, agar private
pengubah berfungsi, Anda harus mendefinisikan kelas ini dalam file Swift yang terpisah.
EDIT: Namun, kode ini tidak memungkinkan untuk mendeklarasikan metode abstrak dan karenanya memaksakan implementasinya.
Cara paling sederhana adalah dengan menggunakan panggilan ke fatalError("Not Implemented")
dalam metode abstrak (bukan variabel) pada ekstensi protokol.
protocol MyInterface {
func myMethod() -> String
}
extension MyInterface {
func myMethod() -> String {
fatalError("Not Implemented")
}
}
class MyConcreteClass: MyInterface {
func myMethod() -> String {
return "The output"
}
}
MyConcreteClass().myMethod()
(MyConcreteClass() as MyInterface).myMethod()
tetapi itu berhasil! Kuncinya termasuk myMethod
dalam deklarasi protokol; jika tidak panggilan akan crash.
Setelah saya berjuang selama beberapa minggu, saya akhirnya menyadari bagaimana menerjemahkan kelas abstrak Java / PHP ke Swift:
public class AbstractClass: NSObject {
internal override init(){}
public func getFoodToEat()->String
{
if(self._iAmHungry())
{
return self._myFavoriteFood();
}else{
return "";
}
}
private func _myFavoriteFood()->String
{
return "Sandwich";
}
internal func _iAmHungry()->Bool
{
fatalError(__FUNCTION__ + "Must be overridden");
return false;
}
}
public class ConcreteClass: AbstractClass, IConcreteClass {
private var _hungry: Bool = false;
public override init() {
super.init();
}
public func starve()->Void
{
self._hungry = true;
}
public override func _iAmHungry()->Bool
{
return self._hungry;
}
}
public protocol IConcreteClass
{
func _iAmHungry()->Bool;
}
class ConcreteClassTest: XCTestCase {
func testExample() {
var concreteClass: ConcreteClass = ConcreteClass();
XCTAssertEqual("", concreteClass.getFoodToEat());
concreteClass.starve();
XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
}
}
Namun saya pikir Apple tidak menerapkan kelas abstrak karena umumnya menggunakan pola + protokol delegasi sebagai gantinya. Misalnya pola yang sama di atas akan lebih baik dilakukan seperti ini:
import UIKit
public class GoldenSpoonChild
{
private var delegate: IStomach!;
internal init(){}
internal func setup(delegate: IStomach)
{
self.delegate = delegate;
}
public func getFoodToEat()->String
{
if(self.delegate.iAmHungry())
{
return self._myFavoriteFood();
}else{
return "";
}
}
private func _myFavoriteFood()->String
{
return "Sandwich";
}
}
public class Mother: GoldenSpoonChild, IStomach
{
private var _hungry: Bool = false;
public override init()
{
super.init();
super.setup(self);
}
public func makeFamilyHungry()->Void
{
self._hungry = true;
}
public func iAmHungry()->Bool
{
return self._hungry;
}
}
protocol IStomach
{
func iAmHungry()->Bool;
}
class DelegateTest: XCTestCase {
func testGetFood() {
var concreteClass: Mother = Mother();
XCTAssertEqual("", concreteClass.getFoodToEat());
concreteClass.makeFamilyHungry();
XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
}
}
Saya membutuhkan pola seperti ini karena saya ingin menyamakan beberapa metode di UITableViewController seperti viewWillAppear dll. Apakah ini membantu?
Ada cara untuk mensimulasikan kelas abstrak menggunakan Protokol. Ini adalah sebuah contoh:
protocol MyProtocol {
func doIt()
}
class BaseClass {
weak var myDelegate: MyProtocol?
init() {
...
}
func myFunc() {
...
self.myDelegate?.doIt()
...
}
}
class ChildClass: BaseClass, MyProtocol {
override init(){
super.init()
self.myDelegate = self
}
func doIt() {
// Custom implementation
}
}
Satu lagi cara Anda bisa mengimplementasikan kelas abstrak adalah dengan memblokir initializer. Saya telah melakukannya dengan cara ini:
class Element:CALayer { // IT'S ABSTRACT CLASS
override init(){
super.init()
if self.dynamicType === Element.self {
fatalError("Element is abstract class, do not try to create instance of this class")
}
}
}
Saya mencoba membuat Weather
kelas abstrak, tetapi menggunakan protokol tidak ideal karena saya harus menulis init
metode yang sama berulang kali. Memperluas protokol dan menulis init
metode memiliki masalah, terutama karena saya menggunakan NSObject
penyesuaian NSCoding
.
Jadi saya datang dengan ini untuk NSCoding
kesesuaian:
required init?(coder aDecoder: NSCoder) {
guard type(of: self) != Weather.self else {
fatalError("<Weather> This is an abstract class. Use a subclass of `Weather`.")
}
// Initialize...
}
Adapun init
:
fileprivate init(param: Any...) {
// Initialize
}
Pindahkan semua referensi ke properti abstrak dan metode kelas Base ke implementasi ekstensi protokol, di mana Self membatasi ke kelas Base. Anda akan mendapatkan akses ke semua metode dan properti kelas Base. Selain itu kompiler memeriksa implementasi metode dan properti abstrak dalam protokol untuk kelas turunan
protocol Commom:class{
var tableView:UITableView {get};
func update();
}
class Base{
var total:Int = 0;
}
extension Common where Self:Base{
func update(){
total += 1;
tableView.reloadData();
}
}
class Derived:Base,Common{
var tableView:UITableView{
return owner.tableView;
}
}
Dengan batasan tidak ada pengiriman dinamis, Anda dapat melakukan sesuatu seperti ini:
import Foundation
protocol foo {
static var instance: foo? { get }
func prt()
}
extension foo {
func prt() {
if Thread.callStackSymbols.count > 30 {
print("super")
} else {
Self.instance?.prt()
}
}
}
class foo1 : foo {
static var instance : foo? = nil
init() {
foo1.instance = self
}
func prt() {
print("foo1")
}
}
class foo2 : foo {
static var instance : foo? = nil
init() {
foo2.instance = self
}
func prt() {
print("foo2")
}
}
class foo3 : foo {
static var instance : foo? = nil
init() {
foo3.instance = self
}
}
var f1 : foo = foo1()
f1.prt()
var f2 : foo = foo2()
f2.prt()
var f3 : foo = foo3()
f3.prt()