The latest instance of the course can be found at: O1: 2024
Luet oppimateriaalin englanninkielistä versiota. Mainitsit kuitenkin taustakyselyssä osaavasi suomea. Siksi suosittelemme, että käytät suomenkielistä versiota, joka on testatumpi ja hieman laajempi ja muutenkin mukava.
Suomenkielinen materiaali kyllä esittelee englanninkielisetkin termit. Myös suomenkielisessä materiaalissa käytetään ohjelmaprojektien koodissa englanninkielisiä nimiä kurssin alkupään johdantoesimerkkejä lukuunottamatta.
Voit vaihtaa kieltä A+:n valikon yläreunassa olevasta painikkeesta. Tai tästä: Vaihda suomeksi.
Chapter 2.2: Inside an Object
About This Page
Questions Answered: How do I implement an (individual) object in Scala? What goes on inside an object when its method is called?
Topics: The program code of a singleton object: an object’s
variables; implementing methods; using this
to refer to oneself.
What Will I Do? A small programming assignment follows a shortish read.
Rough Estimate of Workload:? One hour or a bit more.
Points Available: A50.
Related Projects: IntroOOP.
Introduction
In this chapter, we’ll use Scala to define the behavior of individual objects. As it turns out, that endeavor calls for skills that you already have. This is because an object definition has two main parts:
- An object’s methods are functions attached to the object. To define these functions, we can use techniques that are largely familiar from earlier chapters.
- An object needs to keep track of its attributes. For this purpose, we can use another familiar construct: variables.
The new thing here is how we connect functions and variables to an object.
Our Goal: An Employee Object
Let’s create a singleton object that represents an individual employee in an otherwise imaginary accounting system. But first, let’s see an example of how we intend to use the object.
An employee has attributes such as name and monthly salary; the code that defines the object defines the values of these attributes. We can request those values from the object:
employee.nameres0: String = Edelweiss Fume employee.monthlySalaryres1: Double = 5000.0
The effectful method raiseSalary
expects a number as a parameter. This number is a
multiplier that is applied to the old salary. The following command, for instance, gives
the employee a ten-percent raise:
employee.raiseSalary(1.1)
That command changed the object’s state, as we can verify:
employee.monthlySalaryres2: Double = 5500.0
Given a year as a parameter, the effect-free method ageInYear
sends back a return value
to let the caller know how old the employee will be in that year. The employee object knows
its own year of birth and uses it to compute its age:
employee.ageInYear(2019)res3: Int = 54
The object has a working time, which is defined as a decimal number. The value 1.0 indicates a full-time worker (100-percent working time):
employee.workingTimeres4: Double = 1.0
We can adjust the working time simply by assigning a new value to the attribute (as in the radio example of Chapter 2.1), thereby sending the object a message: “Set your working time to 0.6.” The value 0.6 indicates part-time work; we’ll make our example employee work sixty percent of a full week:
employee.workingTime = 0.6employee.workingTime: Double = 0.6
The effectful monthlyCost
method determines the employee’s monthly cost to their
employer. Our object computes this cost as the product of the monthly salary (now 5500
euros per month), the working time (now 60%), and a multiplier to cover additional costs
(a parameter; 1.3 in the example below):
employee.monthlyCost(1.3)res5: Double = 4290.0
Finally, the description
method returns a string that details the employee’s main
characteristics in English. This effect-free method takes no parameters, so we don’t need
any brackets as we call it (just as we didn’t when we asked the object for its name and
salary above). Here, we ask the object for its description and then print what we get:
println(employee.description)Edelweiss Fume (b. 1965), salary 0.6 * 5500.0 e/month
Defining an Object and Methods in Code
The employee object: how it looks on the inside
Here’s the code that defines our employee object. You can also find the code in
singletons.scala
within the IntroOOP project.
Let’s begin with an overview before we look at each method in detail.
object employee {
var name = "Edelweiss Fume"
val yearOfBirth = 1965
var monthlySalary = 5000.0
var workingTime = 1.0
def ageInYear(year: Int) = year - this.yearOfBirth
def monthlyCost(multiplier: Double) = this.monthlySalary * this.workingTime * multiplier
def raiseSalary(multiplier: Double) = {
this.monthlySalary = this.monthlySalary * multiplier
}
def description =
this.name + " (b. " + this.yearOfBirth + "), salary " + this.workingTime + " * " + this.monthlySalary + " e/month"
}
val
variable
can only be examined (as in employee.yearOfBirth
), but a var
variable can be assigned a new value, as we did with the employee’s
working time in the REPL.def
. There is no rule in Scala that forces us to write
the variables above the methods, but many programmers prefer to do
so. More on the methods below.The employee object: how it works on the inside
The employee’s methods detailed
def ageInYear(year: Int) = year - this.yearOfBirth
def monthlyCost(multiplier: Double) = this.monthlySalary * this.workingTime * multiplier
def raiseSalary(multiplier: Double) = {
this.monthlySalary = this.monthlySalary * multiplier
}
def description =
this.name + " (b. " + this.yearOfBirth + "), salary " + this.workingTime + " * " + this.monthlySalary + " e/month"
this
means: “the object whose method is being
executed”. Or, from the object’s perspective: “I myself”. Follow
the word with a dot, and you can refer to different members of the
object. As shown in the preceding animation, this
is effectively
a parameter variable that works just like other parameter
variables do. It receives its value “from the left of the dot” in
each method call.ageInYear
is
called, respond with a number that you get by subtracting the
value of your own yearOfBirth
variable from the given year.”description
takes no parameters and doesn’t even have a
parameter list.On this
In O1, we’ll use the word this
pretty much every time we wish to
refer to the members (variables and methods) of the object that is
executing a method. In a technical sense, however, there is often no
strict requirement to use the word as we do.
Sometimes it is necessary to use this
. As an example, below
is a different version of the employee object that differs from
our earlier version in just the name of a single variable:
object employee {
var name = "Edelweiss Fume"
val year = 1965
var monthlySalary = 5000.0
var workingTime = 1.0
def ageInYear(year: Int) = year - this.year
// etc.
year
,
not yearOfBirth
, as in the original.ageInYear
’s parameter variable.year
without a preceding this
means the year given as a parameter.this
specifies that we mean the
object’s variable, not the parameter. If
we were to omit it from this version,
the method would subtract its parameter
value from that very same value, and would
therefore invariably return zero.In our original employee object, it would have been possible to
omit each this
prefix, because none of the local variables had
the same name as the object’s variables.
Even when it’s not required, the word this
emphasizes where
the programmer uses an object’s variables, as opposed to where
they use local variables. We recommend that you always use this
when referring to an object’s own variables, because it makes the
code easier to understand.
To an extent, this is a matter of taste. If you wish — and if
you know what you’re doing — go ahead and leave out any this
keywords that don’t need to be there. Outside of O1, such omissions
are common.
Create an Object
Next, you’ll get to write an object that represents a bank account. The object should work as shown in the following example.
The account object: an example scenario
An account has a balance (in euro cents) and an account number. We can ask the object to report this information:
import o1._import o1._ account.balanceres6: Int = 0 account.numberres7: String = 15903000000776FI00
We can deposit money in the account. Here, we choose to represent sums of money as integers that correspond to euro cents. Let’s deposit 200 euros and review the balance:
account.deposit(20000)account.balanceres8: Int = 20000
Depositing a negative sum doesn’t affect the balance:
account.deposit(-1000)account.balanceres9: Int = 20000
The withdraw
method takes money from the account and returns the amount that was
successfully withdrawn:
account.withdraw(5000)res10: Int = 5000
Withdrawals that would result in a negative balance aren’t allowed. Below, we get only 150.00 euros as we empty the account:
account.withdraw(50000)res11: Int = 15000 account.balanceres12: Int = 0
Your assignment
Write an object account
that works precisely as described above. To summarize the main points:
- It has a number
"15903000000776FI00"
. - It has a balance that is initially zero but may change.
- It has a method
deposit
that adds a given amount of money to the account (as long as the given parameter value is positive) and doesn’t return anything. - It has a method
withdraw
that reduces the account’s balance by a given amount, or empties the account in case the given parameter is greater than the current balance. The method returns the amount that was actually withdrawn.- In this assignment, you’re not required to
consider the possibility that a negative
number is passed to
withdraw
. But if you want, you can write the method to leave the account untouched in such cases.
- In this assignment, you’re not required to
consider the possibility that a negative
number is passed to
Write your code within the file o1/singletons.scala
in project IntroOOP. There’s a comment
in the file that shows where exactly.
How to go about it
We suggest the following approach:
- Study the example scenario in detail. Note the names and types of each variable!
- Find the indicated spot in
singletons.scala
. The first line of the object definition and the required curly brackets are already there. - Define the object’s variables inside the curly brackets, assigning values to them. Indent your code with two space characters per line.
- See if it works. Fire up your REPL, choose the IntroOOP project,
enter an
import
, and try the expressionsaccount.balance
andaccount.number
. - Write the methods
deposit
andwithdraw
.- Use the
min
andmax
functions known to you from earlier chapters. - The algorithm you need for
withdraw
should be already familiar from a program in Chapter 1.8.
- Use the
- Reset the REPL session and test your methods on different parameter values. (Reminder: you may find it convenient to use the Relaunch Interpreter and Replay History button in the corner of the REPL.)
- Submit your code only when you’re convinced that your object works as specified.
A+ presents the exercise submission form here.
Something wrong?
Once you’re done with the assignment, think about the object you just defined.
Did you define a var
variable named balance
for your account
object? If you did, that’s all good. But there’s something a bit
questionable about this variable that may have already occurred to
you.
We took it upon ourselves to create an account object whose balance
is modified through deposit
and withdraw
, which apply
due diligence and prevent the balance from becoming negative.
However, there is presently nothing that prevents us from commanding
the object to set its balance to a negative value like this:
account.balance = -100000
Obviously, this is no problem at all if no-one ever issues such a command. We could say that assigning a negative balance counts as an error on the part of whoever uses the account object. However, there are many programmers who agree that programs should be written so that they minimize the risk of such mistakes. Soon enough, in Chapter 3.2, you’ll learn how to restrict the operations on an object’s variables. Right now, you don’t need to worry about the matter.
There are styles of programming where it wouldn’t be considered problematic to make an inappropriate command available. Scala programmers, however, tend to be among those whose who recommend minimizing such opportunities for error so as to be more efficient and build more reliable software.
Summary of Key Points
- Some of the objects in Scala programs are so-called singleton objects: objects defined on an individual basis.
- The definition of a singleton object comprises variable
definitions and method definitions.
- You can define a variable to be part of an object. Such a variable can be assigned values just as we’ve done with other variables.
- You can define methods for an object just as we’ve defined functions before.
- It’s extremely common that a method needs to use one or more of
the object’s own variables or other methods. You can use Scala’s
keyword
this
to make an object refer to itself and its own components. - Links to the glossary: object, singleton object;
this
.
An updated concept map:
Feedback
Please note that this section must be completed individually. Even if you worked on this chapter with a pair, each of you should submit the form separately.
Credits
Thousands of students have given feedback that has contributed to this ebook’s design. Thank you!
Weeks 1 to 13 of the ebook, including the assignments and weekly bulletins, have been written in Finnish and translated into English by Juha Sorva.
Weeks 14 to 20 are by Otto Seppälä. That part of the ebook isn’t available during the fall term, but we’ll publish it when it’s time.
The appendices (glossary, Scala reference, FAQ, etc.) are by Juha Sorva unless otherwise specified on the page.
The automatic assessment of the assignments has been developed by: (in alphabetical order) Riku Autio, Nikolas Drosdek, Joonatan Honkamaa, Jaakko Kantojärvi, Niklas Kröger, Teemu Lehtinen, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä, and Aleksi Vartiainen.
The illustrations at the top of each chapter, and the similar drawings elsewhere in the ebook, are the work of Christina Lassheikki.
The animations that detail the execution Scala programs have been designed by Juha Sorva and Teemu Sirkiä. Teemu Sirkiä and Riku Autio have done the technical implementation, relying on Teemu’s Jsvee and Kelmu toolkits.
The other diagrams and interactive presentations in the ebook are by Juha Sorva.
The O1Library software has been developed by Aleksi Lukkarinen and Juha Sorva. Several of its key components are built upon Aleksi’s SMCL library.
The pedagogy behind O1Library’s tools for simple graphical programming (such as Pic
)
is inspired by the textbooks How to Design Programs by Flatt, Felleisen, Findler, and
Krishnamurthi and Picturing Programs by Stephen Bloch.
The course platform A+ has been created by Aalto’s LeTech research group and is largely developed by students. The current lead developer is Jaakko Kantojärvi; many other students of computer science and information networks are also active on the project.
For O1’s current teaching staff, please see Chapter 1.1.
Additional credits appear at the ends of some chapters.
object
. The next word is a programmer-chosen name for referring to the object.