Problem overview
A class bundles values with the questions you can ask about them. Rectangle remembers a width and a height, and answers three questions: its area() (width times height), its perimeter() (twice the width plus twice the height) and whether it isSquare() (the two sizes are equal).
For new Rectangle(3, 4) the area is 12, the perimeter is 14 and it is not a square. For new Rectangle(5, 5) the answers are 25, 20 and true. Each object keeps its own sizes, so new Rectangle(2, 10) and new Rectangle(10, 2) are two separate objects that give the same area and perimeter.
Approach
Nothing needs searching or looping. The class stores two numbers when the object is created, and every method is one expression over them. Inside a method, plain width and height mean the fields of the object the method was called on, so r.area() and s.area() work with different numbers.
Step by step
- Declare two
intfields,widthandheight. EveryRectanglegets its own copy of each. - Store the constructor's parameters in the fields. The parameters have the same names as the fields, so
this.width = widthmeans "the field gets the parameter". Withoutthis.,width = widthwould assign the parameter to itself and leave the field at 0. areareturnswidth * height.perimeterreturns2 * (width + height). Walking around the rectangle covers each side once, and the parentheses make the addition happen before the multiplication.isSquarereturnswidth == height. A comparison is already aboolean, so noifis needed.
The solution in Java
class Rectangle {
public int width;
public int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int area() {
return width * height;
}
public int perimeter() {
return 2 * (width + height);
}
public boolean isSquare() {
return width == height;
}
}
A few details:
- A constructor has the class's name and no return type.
new Rectangle(3, 4)runs it once to set up the new object. - Use
==only for numbers like these. ForStringand other objects,==compares references, andequalscompares contents. - The tests print booleans as
TrueorFalse. Java's ownprintlnof abooleangivestrueorfalse.
Using the class
With the class above in a file of your own, this code:
Rectangle r = new Rectangle(3, 4);
System.out.println(r.area() + " " + r.perimeter() + " " + r.isSquare());
Rectangle s = new Rectangle(5, 5);
System.out.println(s.area() + " " + s.perimeter() + " " + s.isSquare());
Rectangle t1 = new Rectangle(2, 10), t2 = new Rectangle(10, 2);
System.out.println(t1.area() + " " + t1.perimeter() + " " + t1.isSquare());
System.out.println(t2.area() + " " + t2.perimeter() + " " + t2.isSquare());
prints:
12 14 false
25 20 true
20 24 false
20 24 false
Why each object keeps its own sizes
The class is a blueprint, and new builds one object from it. Each object gets its own width and height, so changing one rectangle never touches another. That is why t1 and t2 above can hold the same numbers in a different order without interfering. The method body is written once, in the class, and runs against whichever object you called it on: in t1.area() the name width means t1.width, and in t2.area() it means t2.width.
The fields are public here, so code outside the class can also read r.width. The class works either way. Later lessons show how private fields protect values that must follow rules, but a rectangle's sizes have no rule beyond being whole numbers.
Special cases
- Equal sizes. A square is a rectangle whose sides match, so
new Rectangle(5, 5).isSquare()istrue. It still has an area and a perimeter like any other rectangle. - Turning a rectangle on its side. Swapping the width and the height changes neither the area nor the perimeter, because multiplication and addition give the same result in either order.
- The smallest rectangle. With width 1 and height 1, the area is 1, the perimeter is 4 and it is a square.
Complexity
Each method does a fixed amount of arithmetic on two numbers, so each runs in O(1) time and the object uses O(1) space. With sizes up to 10,000, the area is at most 100,000,000, which fits in an int.
What the missing this. does
Suppose the constructor is written like this:
public Rectangle(int width, int height) {
width = width;
height = height;
}
It compiles, because inside the constructor the name width refers to the parameter, which hides the field. Each line copies a parameter onto itself and the fields keep their default value, 0. Then new Rectangle(3, 4).area() returns 0 and the field width reads 0. Writing this.width = width names the field on the left side explicitly and fixes it.
Because the program compiles and runs, this mistake shows up only as wrong answers, so a test that checks one known rectangle, such as the 3 by 4 one that gives 12, catches it at once.
Common mistakes
- Forgetting
this.in the constructor. The fields stay 0 and every area is 0. - Writing
2 * width + height. Multiplication goes first, so this counts only one height. Use2 * (width + height). - Using
=instead of==inisSquare. A single=assigns, and Java rejects it as a return value of the wrong type. - Making the fields
static. A static field has one copy shared by all rectangles, so the second rectangle would overwrite the first one's sizes.
