Class data: Getters, Setters & Properties

Data: fields vs properties

Classes have both functions called methods and instance data or  fields. Python also uses the term properties, for data that has an explicit getters function and optionally setter a setter function.

These pages  use the terms methods, fields and properties in this manner.  So fields actually hold the information and provide access to the internal information, while properties use functions to calculate the value they return or what they store, and may not reflect how fields internally represent data. A complication is that is will be seen later that you could with these definitions consider all non-private fields in Kotlin to be also properties.

Methods, including getters and setters, are shared by all members of the class. Fields are specific to each instance of the class, so two different objects of the same class each have their own set of fields.

Boilerplate Getters and Setters.

Languages such as Java have traditionally declared that all fields that are able to be accessed from outside a class, should have explicit getters and setters to ensure encapsulation.  The theory is that a class should be a ‘black box’ and the data fields implementation internally may change. By having a function as the interface to the fields, then the interface can be maintained even when the inner workings of the class change, by changing the getter and setter functions.

The result of this mandating of explicit getters and setters is boilerplate code for ever field visible outside the class.

For both Python and Kotlin, there is a ‘property’ mechanism to add explicit getters and setters while maintaining the same interface as directly accessing a field.

First Python with


# consider in method definition
class Test:
    def __init__(self):
       self.data1 = 1
       self._data2 = 2 # 'backing field' for data2

    @property
    def data2(self):
       return self._data2
    @data2.setter
    def data2(self, value):
      self._data2 = value

test = Test()
print(test.data1 + test.data2)

This will print  ‘3’ ( 1 + 2) and it is clear that the print uses the same interface code for both data1 and data2. Changing from the field to a property has no impact on the code of using the data.  Since the interface will not change, the ‘boilerplate’ in this example is not required, and the property can be added only when  there is more to do than simply return the value of the backing field.

Now the equivalent Kotlin (note ‘var’ used not val to be equivalent to python):


class Test(){
   var data1 = 1
   private var _data2 = 2
   var data2
     get() = 2
     set(value: Int){
     _data2 = value
    }
}
test = Test()
println(test.data1 + test.data2)

Again this would print ‘3’, and again this is clearly a dummy example as again the interface to data1 and data2 from outside the class is unchanged by adding the get and set.

So explicit get and set are only needed with both Python and Kotlin when there is special code for the get and set. Since code external to the class is unchanged by having explicit get() and set(), these could be added at any time with no impact on the class interface.

Cutting the boilerplate, and the impact

In fact,  in Java most getters and setters are unchanged from the boilerplate case of simply returning and directly setting the backing field. but the rule in java is keep the getters and setters, and keep the actual field private.

Modern languages have identified problems with this boilerplate:

  • almost all getters and setters just get or set the value and do nothing else so they just bloat the program
  • it is much clearer for the calling code to get the value of a variable or have an assignment statement to set the value – even when what is happening inside the class is more complex

The solution is:

  • require code only for the non standard getters/setters
  • ensure setting and getting from outside the class looks the same for simple and complex cases
  • use the simple assignment, variable syntax for all cases

Python avoids the need to always have getters and setters by adding the overhead of checking for the optional getters and setters during the access of any field, which results in a small performance penalty.  The nature of Python is that simpler coding is a higher priority than efficiency,  but Kotlin targets to keep the simple coding while still delivering the performance.

The secret to how Kotlin achieves both goals is by Kotlin simply by automatically adding the boilerplate getters and setters internally, so the compiled code is the same as Java, but the programmer writes the same simple code as Python.  How this actually works is described here, but in simple terms when no get or set is specifically coded, the compiler adds the default get and set method.  Beyond the cleaner code, another benefit over the Java type approach, is that as this is all under the control of the compiler, giving the compiler the ability to optimise code to directly access the backing field and bypass the get() and set() whenever appropriate.

So the programmer can leave out the boilerplate for getters and setters in both Python and Kotlin, while maintaining the same encapsulation as other languages that require explicit get and set.

Beyond the boilerplate for getters and setters

As discussed, traditionally java programmers have been taught that encapsulation (a key part of OO) requires building every field of every class with getters and setters, but almost all of this is boilerplate that Python and Kotlin do not need.

Now for those cases where nonstandard getters and setters that are not just boilerplate are needed.

Now for an example of a non-boilerplate getter/setter, first in python :


class Person:
    def __init__(self, name):
      self.name = name
      self.otherName = ""
      self.fullName = name

#now in idle
  tom = Person("Tom") #instance object
  tom.fullName = "Tom Jones" # set property using object
  tom.fullName # get property
'Tom Jones'

With this code, the fullName is also duplicated in name and otherName and the data may not match.  Using getters and setters for fullName allows keeping the same interface, but eliminating the duplication.
Consider:


class Person:
    def __init__(self, name):
        self.name = name
        self.otherName = ""
    @property
    def fullName(self):
        return " ".join([self.name,self.otherName])
    @fullName.setter
    def fullName(self,value):
        if " " in value:
             self.name,self.otherName = value.split(" ",1)
        else:
             self.name = value
             self.otherName = ""

# now in idle
  bob = Person("Bob")
  bob.otherName = "Jones"
  bob.fullName
'Bob Jones'
  bob.fullName = "Bobby Smith"
  bob.name
'Bobby'
  bob.fullName
'Bobby Smith'
  bob.otherName
'Smith'

Now we have the new implementation, and all code written before the change will still work.  Here is the same approach in Kotlin:

data class Person(var name:String) {
// instance variable declared in constructor

    var otherName = "" // an instance variable not from a parameter
    var fullName
        get()= listOf(name,otherName).joinToString(" ")
        set(value) {
            val (first, second) =
                    if(' ' in value) value.split(" " ,limit=2)
                    else listOf(value,"")
            name = first
            otherName = second
        }
}

The kotlin code for having get(ters) and set(ters) requires is more similar to simply declaring a variable. Simply follow the variable (or value) property declaration with the get and/or set methods.

Advertisement

1 thought on “Class data: Getters, Setters & Properties”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s