Swift Override Static Method Compile Error

Swift override static method compile error

In class B, the method list is a separate method from list in class A. They just share the same name, that's all.

The parameters of the two list methods are actually different:

// A.list
static func list(completion: (_ result:[A]?) -> Void) {
// B.list
static func list(completion: (_ result:[B]?) -> Void) {

A.list takes an argument of type (_ result: [A]?) -> Void while B.list takes a (_ result: [B]?) -> Void. The array type in the closure type's parameter list is different!

So you're not overridding anything, you're just overloading.

Note:

static methods can never be overridden! If you want to override a method, use class instead of static.

class A {
class func get(completion: (_ result:A?) -> Void) {
completion (nil)
}
}

class B: A {
override class func get(completion: (_ result:B?) -> Void) {
completion (nil)
}
}

How to prevent subclasses from inheriting super class's static method in Swift?

The comments of my question contains many good answers:

  • there is no dedicated swift feature to prevent subclasses from inheriting super class static methods.

  • move static functions to a new (final) class or struct

  • use class instead of static (=final class) and override the class method and add @available(*, unavailable) to it.

  • use Lint tools

I think the easiest way is to create a new (final) class for static methods which do not depend on subclasses, though, that sacrifices code completion feature a little.

I hope someday Swift will introduce @noInherit static func attribute.

EDIT

I found another way to restrict the way to call a static method in runtime:

class Base {
static func f() {
if Self.self != Base.self {
fatalError("don't call from subclass: " + String(describing: Self.self))
}

print("hello world")
}
}

note: The disadvantage of the EDIT is that it is checked only in runtime. The benefit of it is that you need only 3 lines and you don't need to override the static method in all subclasses. But this way should be used only when you have confident that checking in runtime is enough and safe for your app.

Swift: Overriding Self-requirement is allowed, but causes runtime error. Why?

Yes, there seems to be a contradiction. The Self keyword, when used as a return type, apparently means 'self as an instance of Self'. For example, given this protocol

protocol ReturnsReceived {

/// Returns other.
func doReturn(other: Self) -> Self
}

we can't implement it as follows

class Return: ReturnsReceived {

func doReturn(other: Return) -> Self {
return other // Error
}
}

because we get a compiler error ("Cannot convert return expression of type 'Return' to return type 'Self'"), which disappears if we violate doReturn()'s contract and return self instead of other. And we can't write

class Return: ReturnsReceived {

func doReturn(other: Return) -> Return { // Error
return other
}
}

because this is only allowed in a final class, even if Swift supports covariant return types. (The following actually compiles.)

final class Return: ReturnsReceived {

func doReturn(other: Return) -> Return {
return other
}
}

On the other hand, as you pointed out, a subclass of Return can 'override' the Self requirement and merrily honor the contract of ReturnsReceived, as if Self were a simple placeholder for the conforming class' name.

class SubReturn: Return {

override func doReturn(other: Return) -> SubReturn {
// Of course this crashes if other is not a
// SubReturn instance, but let's ignore this
// problem for now.
return other as! SubReturn
}
}

I could be wrong, but I think that:

  • if Self as a return type really means 'self as an instance of
    Self', the compiler should not accept this kind of Self requirement
    overriding, because it makes it possible to return instances which
    are not self; otherwise,

  • if Self as a return type must be simply a placeholder with no further implications, then in our example the compiler should already allow overriding the Self requirement in the Return class.

That said, and here any choice about the precise semantics of Self is not bound to change things, your code illustrates one of those cases where the compiler can easily be fooled, and the best it can do is generate code to defer checks to run-time. In this case, the checks that should be delegated to the runtime have to do with casting, and in my opinion one interesting aspect revealed by your examples is that at a particular spot Swift seems not to delegate anything, hence the inevitable crash is more dramatic than it ought to be.

Swift is able to check casts at run-time. Let's consider the following code.

let sm = SuperMario()
let ffm = sm as! FireFlowerMario
ffm.throwFireballs()

Here we create a SuperMario and downcast it to FireFlowerMario. These two classes are not unrelated, and we are assuring the compiler (as!) that we know what we are doing, so the compiler leaves it as it is and compiles the second and third lines without a hitch. However, the program fails at run-time, complaining that it

Could not cast value of type
'SomeModule.SuperMario' (0x...) to
'SomeModule.FireFlowerMario' (0x...).

when trying the cast in the second line. This is not wrong or surprising behaviour. Java, for example, would do exactly the same: compile the code, and fail at run-time with a ClassCastException. The important thing is that the application reliably crashes at run-time.

Your code is a more elaborate way to fool the compiler, but it boils down to the same problem: there is a SuperMario instead of a FireFlowerMario. The difference is that in your case we don't get a gentle "could not cast" message but, in a real Xcode project, an abrupt and terrific error when calling throwFireballs().

In the same situation, Java fails (at run-time) with the same error we saw above (a ClassCastException), which means it attempts a cast (to FireFlowerMario) before calling throwFireballs() on the object returned by queryFriend(). The presence of an explicit checkcast instruction in the bytecode easily confirms this.

Swift on the contrary, as far as I can see at the moment, does not try any cast before the call (no casting routine is called in the compiled code), so a horrible, uncaught error is the only possible outcome. If, instead, your code produced a run-time "could not cast" error message, or something as gracious as that, I would be completely satisfied with the behaviour of the language.

Circular warnings about Swift static override being final

Thanks to Martin R above for the link to the issue in the Swift compiler. That issue also has a workaround, that does indeed fix the issue for me.

Fixing this is possible by actually using class instead of static in the override in class Y.

Overriding superclass initializer in Swift

You are not doing anything wrong ;) The override modifier is required because your RailsData superclass declares a designated initializer with the exact same signature:

class RailsData {
init() {...}
...
}

as such, the override modified is made necessary in your subclass. The same thing applies to inherited methods as well. From the The Swift Programming Language book:

When you write a subclass initializer that matches a superclass designated initializer, you are effectively providing an override of that designated initializer. Therefore, you must write the override modifier before the subclass’s initializer definition. This is true even if you are overriding an automatically provided default initializer.

As with an overridden property, method or subscript, the presence of the override modifier prompts Swift to check that the superclass has a matching designated initializer to be overridden, and validates that the parameters for your overriding initializer have been specified as intended.

Rest assured, you won't be bitten by this later on ;)


Motivating example. To see this initializer overriding in action, try this example:

class SuperClass {
init(x: Int) {
print("3. SuperClass.init(x: \(x))")
}
convenience init() {
print("1. SuperClass.init()")
self.init(x: 123) // Will be overridden by subclass.
}
}

class SubClass: SuperClass {
override init(x: Int) {
print("2. SubClass.init(x: \(x))")
super.init(x: x)
print("4. SubClass.init(x: \(x))")
}
}

// Calls inherited convenience initializer.
let sub = SubClass()

outputs:

  1. SuperClass.init()
  2. SubClass.init(x: 123)
  3. SuperClass.init(x: 123)
  4. SubClass.init(x: 123)

Abstract classes. By the way, there is no direct, linguistic support for abstract classes in Swift — nor in Objective-C — but at least Cupertino is thinking about it ;) Currently, such a concept is merely a soft convention used by frameworks authors, etc.

overriding static vars in subclasses swift 1.2

The documentation says:


static
” methods and properties are now allowed in classes (as an alias for “
class final
”).

So it is final, which means you cannot override it.

Marking an open method final in subclass

You can do this by making the method public like this:

open class A { // This class is from the SDK and cannot be modified in anyway.
open func aFunc() {}
}

open class B : A { // This has to be open for others to override.
override final public func aFunc() {}
}

open keyword is for letting subclasses from different module to override, whereas the public keyword only gives access to different module and do not allow the overriding. If you want to override this method in only your module and not in other modules you can just make it public without final.



Related Topics



Leave a reply



Submit