Swift - phương thức lớp phải được ghi đè bởi lớp con


91

Có cách nào tiêu chuẩn để tạo một "hàm ảo thuần túy" trong Swift không. một lớp phải được ghi đè bởi mọi lớp con, và lớp nào, nếu không, gây ra lỗi thời gian biên dịch?


Bạn có thể thực hiện nó trong lớp siêu và đưa ra khẳng định. Tôi đã thấy điều này được sử dụng trong Obj-C, Java và Python.
David Skrundz

8
@NSArray Điều này gây ra lỗi thời gian chạy chứ không phải thời gian biên dịch
JuJoDi

Câu trả lời này cũng sẽ giúp bạn. nhập mô tả liên kết ở đây
Chamath Jeevan

Một hàm ảo thuần tuý được thực hiện bởi protocols (so với interfaces trong Java) Nếu bạn cần phải sử dụng chúng như phương pháp trừu tượng đã nhìn vào câu hỏi này / câu trả lời: stackoverflow.com/a/39038828/2435872
jboi

Câu trả lời:


147

Bạn có hai lựa chọn:

1. Sử dụng một Giao thức

Xác định lớp cha dưới dạng một Giao thức thay vì một Lớp

Pro : Kiểm tra thời gian biên dịch xem mỗi "lớp con" (không phải lớp con thực tế) triển khai (các) phương thức bắt buộc hay không

Con : "Lớp cha" (giao thức) không thể triển khai các phương thức hoặc thuộc tính

2. Khẳng định trong phiên bản siêu của phương pháp

Thí dụ:

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

Pro : Có thể triển khai các phương thức và thuộc tính trong lớp cha

Con : Không kiểm tra thời gian biên dịch


3
@jewirth bạn vẫn sẽ không nhận được một tấm séc thời gian biên dịch trên lớp con
drewag

5
Giao thức không thể triển khai các phương thức nhưng thay vào đó bạn có thể cung cấp chúng thông qua các phương thức mở rộng.
David Moles

2
Kể từ Swift 2.0 bây giờ cũng có các phần mở rộng giao thức :) Tham khảo của Apple .
Ephemera

4
Mặc dù fatalErrorkhông cung cấp tính năng kiểm tra thời gian biên dịch, nhưng thật tuyệt là trình biên dịch ít nhất đủ thông minh để không yêu cầu bạn cung cấp giá trị trả về cho phương thức khi đường dẫn thực thi gọi fatalError.
bugloaf

3
Trường hợp 2: Hãy nhớ một thực tế là nếu bạn gọi super.someFunc()từ phương thức được ghi đè, bạn sẽ gặp lỗi mặc dù thực tế là bạn đã ghi đè nó. Bạn biết rằng bạn không được cho là gọi nó, nhưng người khác không nhất thiết phải biết điều đó và chỉ cần tuân theo thông lệ tiêu chuẩn.
Jakub Truhlář

48

Phần sau cho phép kế thừa từ một lớp và cũng có thể kiểm tra thời gian biên dịch của giao thức :)

protocol ViewControllerProtocol {
    func setupViews()
    func setupConstraints()
}

typealias ViewController = ViewControllerClass & ViewControllerProtocol

class ViewControllerClass : UIViewController {

    override func viewDidLoad() {
        self.setup()
    }

    func setup() {
        guard let controller = self as? ViewController else {
            return
        }

        controller.setupViews()
        controller.setupConstraints()
    }

    //.... and implement methods related to UIViewController at will

}

class SubClass : ViewController {

    //-- in case these aren't here... an error will be presented
    func setupViews() { ... }
    func setupConstraints() { ... }

}

2
đẹp, typealias để giải cứu :)
Chris Allinson

Có cách nào để ngăn người dùng API này lấy ra các lớp clild của họ từ ViewControllerClass thay vì từ ViewController không? Đây là giải pháp tuyệt vời cho tôi bởi vì tôi sẽ bắt nguồn từ bí danh kiểu của mình vài năm nữa và sẽ quên mất những chức năng nào cần được ghi đè vào lúc đó.
David Hiệu trưởng

@David Hiệu trưởng, bạn có thể đặt lớp học của mình ở chế độ riêng tư và các typealias của bạn ở chế độ công khai không? Tin nhắn xin lỗi từ điện thoại của tôi, không thể tự kiểm tra.
ScottyBlades

1
Giải pháp hoàn hảo, cảm ơn bạn vì điều đó. Như được nhấn mạnh bởi @DavidRector, sẽ thật tuyệt nếu có một giải pháp để biến nó thành chỉ các typealias được công khai, nhưng có vẻ như điều đó không thể thực hiện được.
CyberDandy

35

Không có bất kỳ hỗ trợ nào cho lớp trừu tượng / các hàm ảo, nhưng bạn có thể sử dụng một giao thức cho hầu hết các trường hợp:

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

Nếu SomeClass không triển khai someMethod, bạn sẽ gặp lỗi thời gian biên dịch này:

error: type 'SomeClass' does not conform to protocol 'SomeProtocol'

30
Lưu ý rằng điều này chỉ hoạt động đối với lớp trên cùng thực hiện giao thức. Bất kỳ lớp con nào cũng có thể bỏ qua các yêu cầu giao thức.
memmons

2
Ngoài ra, sử dụng Generics trên các giao thức không được hỗ trợ = (
Dielson Sales

14

Một cách giải quyết khác, nếu bạn không có quá nhiều phương thức "ảo", là yêu cầu lớp con chuyển "triển khai" vào phương thức khởi tạo lớp cơ sở dưới dạng các đối tượng hàm:

class MyVirtual {

    // 'Implementation' provided by subclass
    let fooImpl: (() -> String)

    // Delegates to 'implementation' provided by subclass
    func foo() -> String {
        return fooImpl()
    }

    init(fooImpl: (() -> String)) {
        self.fooImpl = fooImpl
    }
}

class MyImpl: MyVirtual {

    // 'Implementation' for super.foo()
    func myFoo() -> String {
        return "I am foo"
    }

    init() {
        // pass the 'implementation' to the superclass
        super.init(myFoo)
    }
}

1
không quá hữu ích nếu bạn có một vài phương pháp ảo hơn
Bushra Shahid

@ xs2bush Nếu nhiều phương thức của bạn là ảo hơn không, có lẽ bạn nên khai báo chúng trong một giao thức và cung cấp những phương thức 'không ảo' thông qua các phương thức mở rộng.
David Moles

1
thats chính xác những gì tôi đã kết thúc làm
Bushra Shahid

0

Bạn có thể sử dụng giao thức so với xác nhận như được đề xuất trong câu trả lời ở đây bằng cách drewag. Tuy nhiên, ví dụ cho giao thức bị thiếu. Tôi đang nói ở đây,

Giao thức

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

Bây giờ mọi lớp con được yêu cầu để triển khai giao thức được kiểm tra trong thời gian biên dịch. Nếu SomeClass không triển khai someMethod, bạn sẽ gặp lỗi thời gian biên dịch này:

lỗi: nhập 'SomeClass' không phù hợp với giao thức 'SomeProtocol'

Lưu ý: điều này chỉ hoạt động đối với lớp trên cùng thực hiện giao thức. Bất kỳ lớp con nào cũng có thể bỏ qua các yêu cầu giao thức. - như nhận xét củamemmons

Quả quyết

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

Tuy nhiên, xác nhận sẽ chỉ hoạt động trong thời gian chạy.


-2

Là người mới phát triển iOS, tôi không hoàn toàn chắc chắn khi nào điều này được triển khai, nhưng một cách để tận dụng tối đa cả hai thế giới là triển khai tiện ích mở rộng cho một giao thức:

protocol ThingsToDo {
    func doThingOne()
}

extension ThingsToDo {
    func doThingTwo() { /* Define code here */}
}

class Person: ThingsToDo {
    func doThingOne() {
        // Already defined in extension
        doThingTwo()
        // Rest of code
    }
}

Phần mở rộng là thứ cho phép bạn có giá trị mặc định cho một hàm trong khi hàm trong giao thức thông thường vẫn cung cấp lỗi thời gian biên dịch nếu không được xác định


1
chức năng trừu tượng là trái ngược với hiện thực mặc định
Hogdotmac
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.