Chapter 5 - Java for Beginners Course

Class Declaration - Class, Methods and Fields

In previous chapters we’ve shown examples illustrating how a class is declared. Let’s recap what we’ve covered so far and add some new concepts that are important when declaring classes.

Class declaration

As we saw in Chapter 1, we declare a class by using the class keyword. In its full form a class declaration can take the following form:

[class modifiers] class ClassName [extends SuperClassName] [implements Interface1, Interface2, ...] {

    // class body (includes class fields, methods, etc)

}
In syntax definitions, like the one above, everything in square brackets is optional.
We’ll cover more class modifiers, the extends and implements keywords and the concept of interfaces in the remaining sections of this chapter.

For example, assume we have a class that represents a Light in your house for an Internet of Things system:

public class Light {
    // class body
}

Declaring fields

Fields are also known as member variables and as mentioned in Chapter 1 they represent the state of our objects. Field declarations can take the following form:

[field modifiers] FieldType variableName [= fieldInitialValue];

For our Light class example above, we can think of three states associated with it, a variable that specifies if the light is on/off, on, a second variable that specifies the color of the light (for example, "WHITE" or "YELLOW"), and a third variable that specifies the power/brightness of the light (think of a dimmable light).

public class Light {
    private boolean on;

    private String color;

    private int power = 0;

    // ...
}
We’re making our fields private to follow best practices and only allow access to the state of the class from inside the class. This is known as Data Encapsulation.
There are other modifiers that we can apply to fields that we’ll cover in upcoming sections of this chapter.

It’s important to note that these fields belong to the instances that we create from the Light class, so each object we create will have its own separate set of values.

For example in this code:

Light light1 = new Light();
Light light2 = new Light();

we are creating 2 separate Light objects, both of which have a value for on, color and power independent from each other. For example, light1 could be turned on and light2 could be turned off, etc.

Declaring methods

Methods represent the behavior of our classes and as covered before are the way for classes in our application to interact with one another.

Method declarations take the following form:

[method modifiers] ReturnType methodName([MethodParameters]) [throws Exceptions] {
    // method body
}
We’ll cover method modifiers later in this chapter. The throws keyword will be covered in a future chapter.

In our Light class, we can add two very basic behaviors, turnOn and turnOff methods that will change the state of our on field:

public class Light {
    //...

    public void turnOn() {
        on = true;
    }

    public void turnOff() {
        on = false;
    }

    //...
}

Method signature and method overloading

The signature of a method in Java is defined by the name of the method and the type of the parameters it receives.

Two methods in the same class can’t have the same signature, otherwise you’ll get a Duplicate method compilation error as the compiler can’t differentiate them.

However, Java supports methods with the same name as long as their list of parameters is different (either in length, or in the types of the parameters it receives). This is known as Method overloading.

In our Light class, we can assume that one of the requirements is to be able to turn the light on and at the same time set the color of the light, the desired power or both. We could use different method names, like turnOnWithColorAndPower, but this becomes very verbose and we can express the same with 4 different turnOn methods:

public class Light {
    //...

    // 1) method with no parameter
    public void turnOn() {
        on = true;
    }

    // 2) method with a String parameter
    public void turnOn(String withColor) {
        color = withColor;
        on = true;
    }

    // 3) method with an int parameter
    public void turnOn(int withPower) {
        on = true;
        power = withPower;
    }

    // 4) method with a String and an int parameter
    public void turnOn(String withColor, int withPower) {
        color = withColor;
        power = withPower;
        on = true;
    }

    //...
}

The code above is perfectly valid as even though the 4 methods have the same name, their list of parameters is different.

When we invoke the turnOn method, Java will invoke the correct method depending on the parameters used, for example, if we do:

Light myLight = new Light();
myLight.turnOn(50);

Java will correctly invoke method #3 in our snippet above as it is the method that matches the parameter type being used. We are passing in 50 which is an int literal value and method #3 expects an int value as the input.

Our Light class is a very basic example. The Light class in this section has design failures that are included to introduce concepts in later sections and see how we can use them to improve the code.

The this keyword and resolving ambiguity

When we are inside an instance method or in a constructor (covered in the next section), Java provides us with a reference that points to the current object called this. With the this reference we can access the instance fields and methods that belong to the object we’re operating on.

The 2 main reasons we’d use the this keyword is 1) to clarify ambiguity and ensure we tell the Java compiler about what field/variable/method we want to operate on, and 2) if we need to pass a reference to the current object to a different method, for example, if we want to print the object in our Console we can do System.out.println(this).

To give an example, let’s take our turnOn(String withColor) method and modify it slightly by changing the name of the parameter to be the same as the name of the field in the class (color):

//...

private String color;

public void turnOn(String color) {
    color = color;
    on = true;
}

//...

This code is valid, as in, it will compile, however the compiler will give us a warning indicating that the color = color assignment has no effect. The reason for this is that because both the method parameter (color) and the class field (color) have the same name, variable shadowing occurs and with the code as it is, it uses the same variable for the left-side and right-side of the assignment.

Java will use the color variable in the closest scope/block of code, in the example above this means the method and hence Java resolves color to be the local variable that comes in as the method argument and not the class field.

In short, we’re telling Java to assign to the color parameter the value it already has, which isn’t really what we want to do. What we want to do is change the class color field’s value to use the value of the color input parameter.

Now, as mentioned above, we can use the this keyword to resolve this type of cases. We can do the following:

public void turnOn(String color) {
    this.color = color;
    on = true;
}

By using this.color Java knows that we’re referring to the color field in the current object and that we want to change its value to the one provided in the color variable that comes as the input parameter to the method.

In the same way we used this.color to refer to a field, it can be used to invoke methods in the current object, for example, this.turnOn(20).

The use of the this keyword isn’t mandatory, but if you have variables with the same name (like the color case above), or methods with the same name where it can be ambiguous either for the Java compiler or for a person reading the code, then using the this keyword is the solution.

It is common practice to use input parameters in a method that have the same name of the fields when the method is simply replacing the value, like in our turnOn(String color) example method above.
In future examples we’ll be using variable shadowing more often so keep in mind the difference between this.x and x when you have more than one variable x available.