Start learning free

LESSON 556 OF 569 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Thermostat in Java: A Private Field With Rules

Practice O(1) Read the full explanation

A thermostat keeps a target temperature, and the target must always stay between 10 and 30 degrees. If any code could change the target directly, nothing would stop it from setting 95. So the target is a private field: only the thermostat's own methods can change it, and every one of them keeps it between 10 and 30.

Implement the class Thermostat:

  • Thermostat() creates a thermostat whose target is 20.
  • setTarget(t) sets the target to t. A value below 10 sets it to 10, and a value above 30 sets it to 30.
  • warmer() raises the target by 1 and cooler() lowers it by 1, but neither ever takes it outside 10 to 30: at 30, warmer() leaves it at 30, and at 10, cooler() leaves it at 10.
  • getTarget() returns the target. It is the only way for other code to read it.

Keep the target private the way the Access Modifiers lesson does in your language: self._target in Python (private by convention), a #target field in JavaScript, and the private keyword in Java, C++, C#, TypeScript, Kotlin, Scala and Swift.

The input is a list of operations, applied in order to one new Thermostat. The output lists what the getTarget calls return, in order. The starter code spells these names the way your language does: set_target and get_target in Python, SetTarget, Warmer, Cooler and GetTarget in C#.

 

Example 1:

Input:
    getTarget()
    warmer()
    warmer()
    getTarget()
    cooler()
    getTarget()
Output: 20 22 21
Explanation: A new thermostat starts at 20. Two warmer() calls take it to 22, and cooler() brings it back to 21.

Example 2:

Input:
    setTarget(35)
    getTarget()
    warmer()
    getTarget()
    cooler()
    getTarget()
Output: 30 30 29
Explanation: 35 is above 30, so the target becomes 30. warmer() can't go past 30, so the target stays 30.
cooler() lowers it to 29.

Example 3:

Input:
    setTarget(4)
    getTarget()
    cooler()
    getTarget()
    setTarget(10)
    warmer()
    getTarget()
Output: 10 10 11
Explanation: 4 is below 10, so the target becomes 10, and cooler() can't go below 10. setTarget(10) keeps 10,
since 10 itself is allowed, and warmer() raises it to 11.

 

Constraints:

  • -1000 <= t <= 1000
  • A test case has from 1 to 1000 operations, and at least one of them is getTarget.
STEP 1 OF 8
    Ctrl + Enter to check

    JAVA · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Thermostat in Java, explained

    Problem overview

    A thermostat keeps a target temperature, and the target must always stay between 10 and 30 degrees. If any code could assign to the target directly, nothing would stop it from setting 95. So the class makes the target private: only the thermostat's own methods can change it, and every one of them keeps it in range.

    • Thermostat() starts with a target of 20.
    • setTarget(t) sets the target to t, but a value below 10 becomes 10 and a value above 30 becomes 30.
    • warmer() raises the target by 1 and cooler() lowers it by 1, never leaving the range 10 to 30.
    • getTarget() returns the target. It is the only way for other code to read it.

    A new thermostat reports 20. After two calls to warmer() it reports 22, and after a cooler() it reports 21. setTarget(35) gives 30, and a following warmer() leaves it at 30.

    Approach: keep the rule in one place

    There are three ways the target can change (setTarget, warmer, cooler), and each must respect the same limits. Writing the range check three times invites a bug in one of them. Instead, only setTarget contains the check. warmer calls setTarget(target + 1) and cooler calls setTarget(target - 1), so at 30 the value 31 is turned back into 30, and at 10 the value 9 is turned back into 10.

    The private keyword makes this reliable. Code outside the class can't bypass the checks, so the rule holds for every thermostat object whatever the rest of the program does. Making a field private and offering methods to read and change it is called encapsulation.

    Step by step

    1. Declare a private int target. Only code inside Thermostat can use it.
    2. The constructor sets target to 20.
    3. In setTarget, handle a value that is too low: if t < 10, set the target to 10. The value is not refused; the target becomes the lowest one allowed.
    4. Handle a value that is too high with else if (t > 30), setting the target to 30.
    5. Otherwise use t as it is. The values 10 and 30 themselves are allowed, so they end up in this branch.
    6. warmer calls setTarget(target + 1).
    7. cooler calls setTarget(target - 1).
    8. getTarget returns target.

    The solution in Java

    class Thermostat {
        private int target;
        public Thermostat() {
            target = 20;
        }
        public void setTarget(int t) {
            if (t < 10) {
                target = 10;
            } else if (t > 30) {
                target = 30;
            } else {
                target = t;
            }
        }
        public void warmer() {
            setTarget(target + 1);
        }
        public void cooler() {
            setTarget(target - 1);
        }
        public int getTarget() {
            return target;
        }
    }
    

    Notes on the Java:

    • The constructor has no parameters, so there is nothing to confuse with the field and this. isn't needed.
    • One method can call another of the same object by name: warmer calls setTarget directly.
    • A getter such as getTarget gives read access without write access.
    • Outside the class, thermostat.target doesn't compile. The compiler reports error: target has private access in Thermostat.

    The three branches cover every integer exactly once: everything below 10, everything above 30, and everything else. Because the branches are chained with else, only one of them runs per call.

    Using the class

    Thermostat th = new Thermostat();
    System.out.println(th.getTarget());
    th.warmer();
    th.warmer();
    System.out.println(th.getTarget());
    th.cooler();
    System.out.println(th.getTarget());
    th.setTarget(35);
    System.out.println(th.getTarget());
    th.warmer();
    System.out.println(th.getTarget());
    th.setTarget(4);
    System.out.println(th.getTarget());
    th.cooler();
    System.out.println(th.getTarget());
    th.setTarget(10);
    th.warmer();
    System.out.println(th.getTarget());
    

    prints:

    20
    22
    21
    30
    30
    10
    10
    11
    

    How setTarget treats each value

    On a new thermostat, calling setTarget(v) and then getTarget() gives these results:

    vtarget afterwardsWhy
    -100010below 10, so the lowest allowed
    910just below the range
    1010the lowest value is allowed as it is
    1111inside the range
    2929inside the range
    3030the highest value is allowed as it is
    3130just above the range
    100030above 30, so the highest allowed

    The same rule covers repeated steps. On a thermostat that starts at 20, calling warmer() 15 times ends at 30, not 35, and then calling cooler() 40 times ends at 10, not -10. Each call stays inside the range, so the limits hold however many calls come.

    Why not check the range in the constructor only?

    The constructor runs once, when the object is created, and it sets a safe value, 20. The danger comes later, every time the target changes. That is why the check lives in setTarget and why every other change goes through it. A rule that is checked in only one of several places is a rule that will eventually be broken.

    Edge cases and common mistakes

    • Refusing instead of clamping. setTarget(35) doesn't leave the old target alone; it sets 30. Likewise setTarget(4) sets 10.
    • Changing target directly in warmer. target++ would run past 30. Going through setTarget reuses the check.
    • Making the field public. The tests would still pass, but any code could then set target = 95, which is exactly what the class is meant to prevent.
    • A getter that changes state. getTarget only returns the value.

    What private protects, and what it doesn't

    private is checked by the compiler, not at run time. A line like th.target = 95 in another class never gets to run, because the program doesn't compile. Inside Thermostat, though, every method can still change target, so the class itself has to follow its own rule: that is why each method either goes through setTarget or never touches the field.

    Each thermostat object has its own target. Raising one thermostat with warmer() leaves any other thermostat at its own value.

    Complexity

    Each method does a few comparisons and one assignment, so every operation is O(1) in time, and the object stores a single integer.

    Keep going with Java500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free