UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Why can you use final for stored properties?

Forums > 100 Days of SwiftUI

I was playing around with the final keyword, and I discovered that I could use it with stored properties:

class Engineer {
    final let hoursWorked = 40
}

What is the purpose of it here? How is this useful and why is this valid code that runs? It doesn't seem that useful.

Obviously, it's useful for the following reasons:

  • Preventing inheritance among classes
  • Preventing overrides among specific methods, computed properties, and property observers

Is there any other place I can use the final keyword?

2      

Always check the Language Guide.

From the section on Overriding Properties:

You can override an inherited instance or type property to provide your own custom getter and setter for that property, or to add property observers to enable the overriding property to observe when the underlying property value changes.

And from the section on Preventing Overrides:

You can prevent a method, property, or subscript from being overridden by marking it as final. Do this by writing the final modifier before the method, property, or subscript’s introducer keyword (such as final var, final func, final class func, and final subscript).

2      

I took a look at some of that documentation a bit closer and did some additional research. I've determined that there's really only two scenarios where you would want to use the final keyword:

  • To block class inheritance
  • To block overrides

The same as I had in the first post.

When considering stored properties, Swift does not allow you to override them as there is no behavior to override. Thus, it doesn't seem to make much sense to use the final keyword with them. Stored properties aren't classes and you cannot override anything with them, which still makes my initial question valid. They don't seem that useful in this specific context.

If it doesn't make sense to override a stored property, then what's the point of being able to still add the keyword in front of a stored property?

I'm still at the conclusion that using final in this specific context is completely useless, and I just wonder why it would be allowed for simple stored properties if it isn't useful.

The documentation refers to properties in the context of them actually having behavior to override. If they're stored properties then there's nothing to override... so there's no point in the keyword. The more and more I play with final on stored properties, the more and more I realize that they work the exact same way with or without the keyword. 😅

Update:

Perhaps this is the reason why? You could do something like this:

class Engineer {
    var hoursWorked: Int = 40
}

class SoftwareEngineer: Engineer {
    override var hoursWorked: Int {
        didSet {
            hoursWorked = hoursWorked + 5
        }
    }
}

let softwareEngineer = SoftwareEngineer()
softwareEngineer.hoursWorked = 45
print(softwareEngineer.hoursWorked)

I have a stored property in the Engineer class, which will then be overridden to have a didSet property observer added on to it. That could possibly yield the need for a final in front of the hoursWorked stored property in the Engineer class, right? I could stop this override if I wanted to with the keyword, which gives a reason for it.

2      

You can override a stored property with a computed property. (Obviously in this particular example you can't, because you've declared hoursWorked as a constant using let.)

class Engineer {
    var hoursWorked = 40
}

class NetworkEngineer: Engineer {
    override var hoursWorked: Int {
        get { 60 }
        set { }
    }
}

But if you declare hoursWorked with the final modifier, then you won't be able to do that.

2      

Thanks. Just to clarify, I was talking about how you can't override stored properties with other stored properties.

I can see how what I stated earlier could be a bit confusing:

When considering stored properties, Swift does not allow you to override them as there is no behavior to override. Thus, it doesn't seem to make much sense to use the final keyword with them.

What I meant by that was something like this:

class NetworkEngineer: Engineer {
    override var hoursWorked: Int = 50 // Error: Cannot override with a stored property 'hoursWorked'
}

There is no new behavior being added to hoursWorked, which produces an error. I guess it's not that there isn't any behavior to override, it's that stored properties and the way they are designed do not enable any new behavior to be implemented, without becoming computed properties or adding on property observers. Property observers cannot be thought of as true "behavior" because they are limited to what happens only when you write and there are missing capabilities there (like when you read). Thus, stored properties can't really "override" anything as they can't offer any behavior to add on their own. At least that could be a possible theory. As you'll see later on, it seems debatable. It's probably easy to mix up overriding with changing behavior compared to changing values of properties, like in this example.

While I was looking through this a bit more, I started to think of a few more things that I would like to clarify:

  • Based off of observing your code, could it be safe to say that property observers could be thought of as just "additions" to computed properties? In Swift, the two kinds of properties are stored properties and computed properties. Does that mean that property observers are just a type of computed property... or they're additional parts to a computed property?
  • Why does the setter need to be there? Is it because hoursWorked is a variable and therefore I can change the value of it, which prompts a need for a certain behavior (even if it is nothing) to happen when it is written to?

When looking over all of this, it really seems like I just left out a small detail in my very first post. I originally said there are two scenarios where final can be useful:

  • Preventing inheritance among classes
  • Preventing overrides among specific methods, computed properties, and property observers

I think this entire misconception can be fixed with a simple change to my original statement:

The final keyword can be useful in two cases:

  • Preventing inheritance among classes
  • Preventing overrides among specific methods, computed properties, property observers, and stored properties
    • Important: When considering computed properties and property observers specifically, the final keyword can be used not only in the context of blocking the overriding of corresponding computed properties and property observers of a parent class, but ALSO in the context of blocking the overriding of stored properties of a parent class that can have additional behavior added to them either through a computed property getter/setter or willSet/didSet property observers.

Therefore, there is an actual purpose for using final on stored properties of parent classes.

I forgot about that second detail, and I think knowing that small thing answers my question. Please let me know what you think of this conclusion! :)

While on this subject, it could be helpful to map out what can and cannot override what. I am certain this is correct (and tested code to check):

Computed Properties:

  • Computed properties can override other computed properties
  • Computed properties can override stored properties
  • Computed properties can override property observers

Stored Properties:

  • Stored properties cannot override other stored properties, computed properties, or property observers (they don't override at all... I tried researching why this was and I got a little confused... it seems debatable)

Property Observers:

  • Property observers can override other property observers
  • Property observers can override stored properties
  • Property observers can override computed properties

The use of final could block any of these types of overrides.

Sorry! This post is a bit long, but I also just view of these forums as my own notes as well! :)

2      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.