When I try to override a property I get an error "can not override mutable property with read-only property"
I have provided get and set in the super class.
class Card {
var contents:String {
get {
return self.contents
}
set {
self.contents = newValue
}
}
init() {
self.contents = ""
}
}
Here is my Subclass where I am trying to override the "contents" property.
class PlayingCard: Card {
override var contents:String { //<-- this is where I get the build error
get {
var rankStrings:Array<String> = PlayingCard.rankStrings()
return rankStrings[Int(self.rank)] + self.suit
}
}
}
What exactly am I doing wrong?
If the property you're overriding has both a getter and a setter, you need to provide both in your subclass as well. Here's the relevant part from the Swift language guide (emphasis mine):
You can present an inherited read-only property as a read-write
property by providing both a getter and a setter in your subclass
property override. You cannot, however, present an inherited
read-write property as a read-only property.
If you're not doing anything special with the value, then you'll typically want to pass the value being set on to the base class:
set {
super.contents = newValue
}
You could also just discard the value with an empty setter (although I can't think of a good reason to do this offhand):
set { }
I also wanted to point out that you have an infinite loop in the contents property in your Card class. When you you do this:
get {
return self.contents
}
You're actually just calling that same getter again, creating an infinite loop; you're doing the same with the setter. Swift doesn't create ivars for your properties automatically like Objective-C did, so you need to create them yourself. A more appropriate way to create that property would be to do something like this:
class Card {
private var _contents: String
var contents: String {
get {
return _contents
}
set {
_contents = newValue
}
}
init() {
_contents = ""
}
}
However, since you're not doing anything other than setting and returning _contents in your setter and getter, you can simplify it down to this:
class Card {
var contents: String = ""
init() {
}
}
Note: contents might also be a good candidate for using an optional (String?) and setting it to nil rather than initializing it to an empty string.
The compiler error message is fairly straightforward: Card's contents property is mutable, which is to say it has a set method in addition to the get method.
Your override only adds a get method, you need to add a set method too.
I think this is what you want:
set(newValue) {
rankStrings[Int(self.rank)] = newValue;
}
Related
In java you can do the follwing:
public class Foo {
private String bar = "text";
public void method() {
// direct access (no logic)
System.out.println(this.bar);
}
// only if you access the object from the outside
// you are forced to use the getter with some logic in it
public String getBar() {
System.out.println(this.bar);
return this.bar;
}
}
But if you define a getter or a setter with logic in Kotlin you are forced to always execute this logic when accessing the field:
class Foo {
var bar: String = "text"
get() {
println(field)
return field
}
private set
fun method() {
// this also executes the getter
// Is it possible to skip the getter
// and directly access the field?
println(this.bar)
}
}
Is there a better way to access the field without executing the getter or setter logic than creating your own fun getBar() in Kotlin?
There is no possible way to skip a getter or a setter, they are intended to block the direct access of a property.
What you can do is make a multi-reference to same value (fake-referencing):
private var _bar: String = "text"
var bar
get() {
// some operations intercepting the getter
return _bar
}
// direct access
_bar
// intercepted access public field
bar
In Kotlin the backing fields (in your case the private variable) are not exposed by design. There are a few exceptions explained here: https://kotlinlang.org/docs/reference/properties.html#backing-fields
All access to val and var happens through implicit getters and setters. A val resolves to a property with a getter() while var resolves to a property with a getter and a setter: https://kotlinlang.org/docs/reference/properties.html#properties-and-fields
I have a simple boolean variable in a function on which a set an observer to check when it changes:
var userValid: Bool{
didSet{
let valid=userValid ? "valid" : "not valid"
print("uservalid changed to \(valid)")
}
}
I set it to false in the init and then to true in the program: the initialiser is called twice once for the class and once for the subclass but I am alway checking the value in the class. So I am seeing the variable is set to true in the observer, but then when I check it in another function I find it at false without the observer to be ever called again.
As it comes out the problem seems to stem from the fact I initialise two times the class: once as a singleton and the other one with:
init?(coder aDecoder: NSCoder)
Of course I initialise the singleton with:
public class func sharedMapDelegate() -> MapDelegate {
struct Static {
static let instance : MapDelegate = MapDelegate()
}
return Static.instance
}
How may I return the value generated from the initWithCoder instead of creating a new one?
I think I have fixed it by changing the singleton to:
public class func sharedMapDelegate() -> MapDelegate {
struct Static {
static var instance : MapDelegate {
if let me = Factory.sharedFactory().mapHandler {
return me
} else {
Factory.sharedFactory().mapHandler=MapDelegate()
return Factory.sharedFactory().mapHandler
}
}
}
return Static.instance
}
I have been reading about properties in Kotlin, including custom getters and setters.
However, I was wondering if it is possible to create a custom getter with extra parameters.
For example, consider the following method in Java:
public String getDisplayedValue(Context context) {
if (PrefUtils.useImperialUnits(context)) {
// return stuff
} else {
// return other stuff
}
}
Note that the static method in PrefUtils has to have Context as a parameter, so removing this is not an option.
I would like to write it like this in Kotlin:
val displayedValue: String
get(context: Context) {
return if (PrefUtils.useImperialUnits(context)) {
// stuff
} else {
// other stuff
}
}
But my IDE highlights all of this in red.
I am aware I can create a function in my class to get the displayed value, but this would mean I would have to use .getDisplayedValue(Context) in Kotlin as well instead of being able to refer to the property by name as in .displayedValue.
Is there a way to create a custom getter like this?
EDIT: If not, would it be best to write a function for this, or to pass Context into the parameters of the class constructor?
As far as I know, property getter cannot have parameter. Write a function instead.
You can do this by having a property that returns an intermediate object that has a get and/or set operator with the parameters that you want, rather than returning the value directly.
Having that intermediate object be an inner class instance may be useful for providing easy access to the parent object. However, in an interface you can't use inner classes so in that case you might need to provide an explicit constructor parameter referencing the parent object when constructing your intermediate object.
For instance:
class MyClass {
inner class Foo {
operator fun get(context: Context): String {
return if (PrefUtils.useImperialUnits(context)) {
// return stuff
} else {
// return other stuff
}
}
}
val displayedValue = Foo()
}
...
val context : Context = whatever
val mc : MyClass = whatever
val y: String = mc.displayedValue[context]
You can do for example:
val displayedValue: String by lazy {
val newString = context.getString(R.string.someString)
newString
}
I would like to use a closure as a computed property. I mean something like the code below.
class MyClass {
typealias myFuncType = () -> (Void)
private var _myAction:myFuncType
var myAction:myFuncType = {
set(newAction){
self._myAction = newAction
}
}
}
Is it possible or will the compiler think that as I opened a bracked it must be the closure definition?
Closures (and functions) are advertised as first class citizens in swift, so you can store them in variables and properties like any other data type.
That said, your code is almost good, you just have to remove the '=' because otherwise it's considered as a stored property with inline initialization. The correct code is:
var myAction:myFuncType {
set(newAction) {
self._myAction = newAction
}
get { // see notes below
return _myAction
}
}
Some notes:
there's no need to use a computed property backed by a stored property - your code is equivalent to:
class MyClass {
typealias myFuncType = () -> (Void)
var myAction: myFuncType
}
if you need to make additional processing when setting the property, make use of Property Observers: willSet and didSet
in a computed property, if you implement a setter, you must also provide a getter
In Objective-C I am capable of doing the following on properties:
#property (nonatomic, strong) Person *currentPerson;
Then intellisense will reveal that I can add the following method:
- (void)setCurrentPerson:(Person *)currentPerson {
...
}
When that properties method is set, that method is then called.
Is there an equivalent way of having setter methods fired in Swift? I'm noticing that there's no intellisense and adding the same method in Swift never gets called.
func setCurrentPerson(currentPerson: Person) {} // never called
You can achieve the same result by using a pseudo-private property embedded into a computed property, as follows:
class Test {
var _data: String = ""
var data: String {
get { return self._data}
set { self._data = newValue }
}
}
the _data property is still publicly accessible (there's no private/protected/private access modifier in swift yet) - that's the reason why I prefix it with an underscore, as a reminder for me that's a pseudo-private property.
With the data computed property you have getter and setter you can use to override the standard behavior.
This method is a workaround mimicking properties in objective c. willSet and didSet property observers can still be used as outlined by #gotnull in his answer.
As defined in the Swift Properties guide:
var currentPerson: Person? {
willSet(currentPerson) {
// ...
}
didSet {
// ...
}
}