Search code examples
swiftinheritancevirtualvirtual-functions

Is there any way to approach the same functionality without "virtual" keyword in swift


Absence of "virtual" keyword and consequences in swift.

Hello everyone. Because there is no "virtual" keyword I don't know that how can I create a subclass which has 2 methods with 2 rule for each one:

First method:

  1. If object was created from its own class, it has to call its own method.
  2. If there is an upcasting, it has to call the base class's method

Second method:

  1. If object created from its own class, it has to call its own method.
  2. If there is an upcasting, it has to call its own method again.

Let me explain with c# code

// <BEGIN_IMPORTANT_PART>
Console.WriteLine("From base_derived");
Base base_derived = new Derived();
base_derived.ifPolyCallBase(); // nonOverridedFromBase
base_derived.ifPolyCallDerived(); // overridedFromDerived
// <END_IMPORTANT_PART>

Console.WriteLine("\nFrom base");
Base @base = new Base();
@base.ifPolyCallBase(); // nonOverridedFromBase
@base.ifPolyCallDerived(); // overridedFromBase

Console.WriteLine("\nFrom derived");
Derived derived = new Derived();
derived.ifPolyCallBase(); // nonOverridedFromDerived
derived.ifPolyCallDerived(); // overridedFromDerived

class Base
{
    virtual public void ifPolyCallBase()
    {
        Console.WriteLine("nonOverridedFromBase");
    }
    virtual public void ifPolyCallDerived()
    {
        Console.WriteLine("overridedFromBase");
    }
}

class Derived : Base
{
    public void ifPolyCallBase()
    {
        Console.WriteLine("nonOverridedFromDerived");
    }
    override public void ifPolyCallDerived()
    {
        Console.WriteLine("overridedFromDerived");
    }
}

In swift when i use override it calls only derived method, we cannot use without override too, and i cannot delete the method too because of other functionalities as you see. So my questions are:

  1. What is the equivalent code in swift (you can use protocol-struct instead of classes if it is preferable)
  2. If there is no "virtual" keyword, I think there is a good reason, could you explain why?

Thanks!

With closest swift code I tried to explain what I want. Is there any way to change first output without change other ones.

class Base {
    func ifPolyCallBase() {
        print("FromBase")
    }
    func ifPolyCallDerived() {
        print("FromBase")
    }
}

class Derived: Base {
    override func ifPolyCallBase() {
        print("FromDerived")
    }
    override func ifPolyCallDerived() {
        print("FromDerived")
    }
}

let base_derived: Base = Derived()
base_derived.ifPolyCallBase() // FromDerived (PROBLEM: But I want to see output "FromBase" without change other outputs)
base_derived.ifPolyCallDerived() // FromDerived

let base: Base = Base()
base.ifPolyCallBase() // FromBase
base.ifPolyCallDerived() // FromBase

let derived: Derived = Derived()
derived.ifPolyCallBase() // FromDerived
derived.ifPolyCallDerived() // FromDerived


Solution

  • In Swift, as in many languages which support inheritance, methods defined on classes are always dynamically-dispatched (virtual); Swift does not have functionality which allows you to differentiate between whether a method was called on Derived while its static type was Derived or Base.

    However, Swift does offer static dispatch in two ways, which may help you achieve what you're after:

    1. static methods, defined at the type level and not on instances, are always statically-dispatched. If you define a static method on your Base class which takes an instance of Base, you can centralize the definition and decide what to do with an instance there:

      class Base {
          static func ƒ(with instance: Base) { … }
      }
      
      class Derived: Base {}
      
      Base.ƒ(with: Base())
      Dervied.ƒ(with: Derived())
      

      There is a downside to this approach, though: static methods cannot be overridden by subclasses. This means that you can get b: Base = Base() and b: Base = Derived() behavior, but not d: Derived = Derived() behavior — because you will only ever be calling the Base method

    2. There is one incidental alternative that does allow you to implement what you describe more directly: methods defined in extensions to protocols (but which are not protocol requirements, defined in the original protocol definition itself) are also always statically-dispatched. If you take the Base method, define its implementation on a marker protocol, and conform Base to it, you can get the method to statically-dispatch when called on Base:

      protocol P {}
      extension P {
          // This method _must_ be defined in an extension, and not exist in the
          // declaration of `P` or else it will by dynamically-dispatched.
          func ƒ() {
              print("Base")
          }
      }
      
      class Base: P {} /* "inherit" `P.ƒ()` */
      
      class Derived: Base {
          func ƒ() {
              print("Derived")
          }
      }
      
      Base().ƒ() // => "Base"
      Derived().ƒ() // => "Derived"
      (Derived() as Base).ƒ() // => "Base"
      

      It's important to note that the reason this works is that P.ƒ() and Derived.ƒ() are two entirely separate methods, which just happen to have the same name, but based on context, static dispatch picks one or the other. Derived.ƒ() does not override P.ƒ() — it just happens to be called with the same name. This does have the consequence that Derived.ƒ() is dynamically dispatched: if you need multiple layers of inheritance (e.g., DerivedDerived), this approach on its own will not be sufficient.