Problem overview
A class bundles some values with the questions you can ask about them. Here the values are a rectangle's width and height, and the questions are its area, its perimeter and whether it is a square. Each Rectangle object keeps its own sizes, so two rectangles never get in each other's way.
For a 3 by 4 rectangle the area is 12, the perimeter is 3 + 4 + 3 + 4 = 14, and it is not a square. For 5 by 5 the area is 25, the perimeter is 20, and it is a square. A rectangle turned on its side, 2 by 10 against 10 by 2, has the same area and perimeter.
The class header does half the work
The starter code already declares the fields in the class header:
class Rectangle(val width: Int, val height: Int) {
The parameters in parentheses are the constructor's parameters, and the word val also turns each of them into a read-only field of every object. So new Rectangle(3, 4) creates an object whose width is 3 and whose height is 4, and inside a method you can use width and height directly. They always mean the fields of the object the method was called on.
Step by step
- area: return
width * height. A 3 by 4 rectangle is 3 rows of 4 unit squares. - perimeter: walking around the rectangle you pass each side once, so the length is
width + height + width + height, which is2 * (width + height). The parentheses make the addition happen first. Without them,2 * width + heightwould be a different number. - isSquare: return
width == height. A comparison already is aBoolean, so noifis needed. In Scala,==compares values.
The solution in Scala
class Rectangle(val width: Int, val height: Int) {
def area(): Int = {
return width * height
}
def perimeter(): Int = {
return 2 * (width + height)
}
def isSquare(): Boolean = {
return width == height
}
}
The Scala details worth knowing:
- The result type comes after the parameters.
def area(): Intsays the method takes nothing and gives back anInt. The body in braces follows the=. returnis optional. The last expression of a block is its value, sowidth * heightalone would do. The reference usesreturn, which works the same.valfields can't be reassigned. Writingr.width = 5is a compile error ("Reassignment to val width"). The rectangle's sizes are fixed when it is created.- Integer arithmetic.
Intis 32 bits. With sizes up to 10,000 the area is at most 100,000,000, which fits easily.
Using the class
Create the objects with new, then call the methods:
val r = new Rectangle(3, 4)
println(s"${r.area()} ${r.perimeter()} ${r.isSquare()}")
Running it for four rectangles gives:
Rectangle(3, 4): 12 14 false
Rectangle(5, 5): 25 20 true
Rectangle(2, 10): 20 24 false
Rectangle(10, 2): 20 24 false
Scala writes its booleans as false and true; the lesson's tests print them as False and True.
Each object keeps its own sizes. After val a = new Rectangle(3, 4) and val b = new Rectangle(5, 5), the expression a.width is 3 while b.width is 5.
Complexity
Each method does one or two arithmetic operations on the two fields, so each one runs in O(1) time and the object needs O(1) space.
Common mistakes
- Forgetting the parentheses in the perimeter.
2 * width + heightcounts the height only once. - Using
=instead of==inisSquare. A single=assigns, and sincewidthis avalit doesn't compile. - Confusing area and perimeter. Area multiplies, perimeter adds. A 2 by 10 rectangle has area 20 and perimeter 24.
- Thinking a square needs its own class. A square is a rectangle whose sides are equal, so a method that compares the two sizes is enough.
