2017-01-18 7 views
0

Эти протоколы дают мне кошмары.Swift, классы по расширенному протоколу не соответствуют оригинальному протоколу

Я пытаюсь реализовать пару протоколов и классов, соответствующих им, так что я могу иметь реализации по умолчанию, но настраиваемые реализации доступны путем расширения протоколов/классов. До сих пор, это то, что у меня есть:

protocol ProtA { 
    var aProperty: String { get set } 
    var anotherProperty:String { get set } 

    func aFunc (anArgument: String) -> String 
} 

protocol ProtB: ProtA { 
    var aThirdProperty: String { get set } 
} 

protocol ProtC { 
    func doSomething(parameter: Int, with anotherParameter: ProtA) 
} 

class ClassA: ProtA { 
    var aProperty: String = "Hello, I'm a String." 
    var anotherProperty: String = "I'm not the same String." 

    func aFunc (anArgument: String) -> String { 
     return anArgument 
    } 
} 

class ClassB: ProtB { 

    var aProperty: String = "Hello, I'm a String." 
    var anotherProperty: String = "I'm not the same String." 
    var aThirdProperty: String = "I'm yet another String!" 

    func aFunc (anArgument: String) -> String { 
     return anArgument 
    } 
} 



class ClassC: ProtC { 

func doSomething(parameter: Int, with anotherParameter: ProtA) { 
    print (anotherParameter.aProperty) // Works fine. 

} 

} 

Тогда, если я

class ClassC: ProtC { 

    func doSomething(parameter: Int, with anotherParameter: ProtA) { 
     print (anotherParameter.aProperty) // Works fine. 

    } 

} 

Но, если я

class ClassD: ProtC { 

    func doSomething(parameter: Int, with anotherParameter: ProtA) { 

     print (anotherParameter.aThirdProperty) // Value of type 'ProtA' has no member 'aThirdProperty' 

    } 

} 

и, если вместо этого я

class ClassE: ProtC { 

    func doSomething(parameter: Int, with anotherParameter: ProtB) { 

     print (anotherParameter.aThirdProperty) // Type 'ClassE' does not conform to protocol 'ProtC' 

    } 

} 

Что я делаю неправильно?

+2

Ну 'ProtA' не требует' aThirdProperty' - 'ProtB' делает. Вы не можете соответствовать 'ProtC' методом' func doSomething (параметр: Int, with anotherParameter: ProtB) ', поскольку' anotherParameter' должен иметь тип 'ProtA', как требует протокол. – Hamish

ответ

2

Нет ничего плохого. Вы должны просто убедиться, что декларации семантически согласованы. Вы должны либо создать ProtD, объявив метод параметром ProtB, либо развернув полученный параметр ParamA, чтобы использовать его как ProtB.

func doSomething(parameter: Int, with anotherParameter: ProtB) { 
    if let a = anotherParameter as? ProtA { 
      print (a.aThirdProperty) 
    } 

} 
+0

Это действительно работает! Это не очень красиво, но это работает. –

4

Проблема

При наследовании от типа, вы не может сузить типы параметров, используемых в перекрытых функций. Это вы сделали, изменив параметр из типа ProtA (более общий тип), на ProtB (более конкретный тип).

Это является следствием Liskov substitution principle. Проще говоря, подкласс должен иметь возможность делать (минимум) все, что может сделать суперкласс.

ProtC устанавливает, что все соответствующие типы имеют функцию func doSomething(parameter: Int, with anotherParameter: ProtA), с типом (Int, ProtA) -> Void).

Ваша измененная функция в ClassE имеет тип (Int, ProtB) -> Void. Однако эта функция больше не может служить заменой той, которую она переопределяет.

Предположим, что можно было сделать то, что вы пробовали. Смотрите, что случилось бы:

let instanceConformingToProtA: ProtA = ClassA() 

let instanceConformingToProtC: ProtC = ClassE() 

// This would have to be possible: 
instanceConformingToProtC(parameter: 0, amotherParameter: instanceConformingToProtA) 

Но ClassE()не может принять instanceConformingToProtA в качестве действительного аргумента второго параметра, потому что это ProtA, не требуемого ProtB.

Решение

Решение этой задачи полностью зависит от того, что вы пытаетесь достичь. Мне нужна дополнительная информация, прежде чем вы сможете продолжить.

Как правило, при переопределении унаследованных членов:

  • типы параметров должны быть одинаковыми, или более общим.
    • E.g. вы не можете переопределить функцию с параметром типа Car и изменить тип параметра на RaceCar. Это приводит к тому, что ваши классы могут работать с RaceCar s, которые он должен выполнять LSP.
    • E.g. вы можете переопределить функцию с параметром типа Car и изменить параметр на Vehicle. Это позволяет сохранить способность ваших классов работать с «Транспортными средствами».
  • Обратные типы должны быть одинаковыми или более конкретными.
    • E.g. вы не можете переопределить функцию с возвращаемым типом Car с функцией, которая возвращает Vehicle. Это означает, что возвращаемое значение будет «менее мощным», чем гарантирует суперкласс.
    • E.g. вы можете переопределить функцию с возвращаемым типом Car с функцией, которая возвращает RaceCar. Это означает, что возвращаемое значение является «более мощным», и оно, по крайней мере, равно тому, что и суперматериальные guarentees.
+0

typo in: «должен быть в состоянии сделать (как минимум) все, что может выполнять подкласс»: должен быть «суперкласс». – shallowThought

+0

@shallowThought Спасибо. Исправлена. В будущем, не стесняйтесь предлагать редактирование :) – Alexander

+0

Я не хотел изменять ваш ответ. – shallowThought

 Смежные вопросы

  • Нет связанных вопросов^_^