In this chapter, we are going to implement the design from the last chapter. First, we'll check how the concrete logic workflow is implemented inside the main function that starts the program. Then we'll see how the operations are implemented in a class.
Implement the logic workflow by sending messages
Phase 1: creating an object
First, you have to create a canvas for your work and add the main method:
public class MorningRoutine {
public static void main(String[] args) {
}
}
Remember that the public static void main(String[] args)
method is the starting point of the program. Everything that happens is defined between its opening curly brace {
and its closing brace }
. The main
method is located in aMorningRoutine
class. This means that when the code is compiled, the file used to start the program will be named MorningRoutine.class.
In practice, that means you can open the terminal on your own computer, go to the folder where your code is located, and enter the following command:
javac MorningRoutine.java TodoList.java
This will compile the code and generate the file: MorningRoutine.class. You will then be able to run the program with the following command:
java MorningRoutine
Now you've got to create your object. Use the class we've designed to make an object:
public class MorningRoutine {
public static void main(String[] args)
{
TodoList myTodoList = new TodoList("My morning routine");
}
}
The TodoList myTodoList = new TodoList("My morning routine");
statement creates an object that is an instance of the TodoList class. This instance is assigned to a myTodoList
variable. As you can see, you are sending a "My morning routine"
string as an argument. This string corresponds to the topic of the TodoList and is assigned to the topic
attribute.
At this point, the myTodoList
object has been created and is ready to receive messages. This means you can apply all the operations we discussed in the last chapter. 🤘
Phase 2: setting up the regular morning routine
You will first send messages adding regular morning routine tasks to your to-do list:
public class MorningRoutine {
public static void main(String[] args)
{
TodoList myTodoList = new TodoList("My morning routine");
myTodoList.addTask("Wake up");
myTodoList.addTask("Shower");
myTodoList.addTask("Have breakfast");
myTodoList.addTask("Go to work");
}
As you can see, a message really means calling a method in the object with the appropriate arguments. You can now check that everything worked properly by calling the display()
method.
public class MorningRoutine {
public static void main(String[] args)
{
TodoList myTodoList = new TodoList("My morning routine");
myTodoList.addTask("Wake up");
myTodoList.addTask("Shower");
myTodoList.addTask("Have breakfast");
myTodoList.addTask("Go to work");
myTodoList.display();
}
}
This generates the following output:
Here is our Todo List for My morning routine: Task 0 is Wake up Task 1 is Shower Task 2 is Have breakfast Task 3 is Go to work
Have you noticed how the Display()
method takes no argument and still manages to get the work done? If you recall, a method has access to all the fields of the class it is defined in! This is what makes them so useful. ✨
Now that your regular morning routine has been stored, apply the changes for the day!
Phase 3: making changes to your daily routine
Start by removing breakfast and displaying the list again to make sure it worked.
When the list at the end of phase two was displayed, you saw that your Have breakfast
task was located at position 2. You can use that information to send a remove message. Then display the list again:
public class MorningRoutine {
public static void main(String[] args)
{
TodoList myTodoList = new TodoList("My morning routine");
myTodoList.addTask("Wake up");
myTodoList.addTask("Shower");
myTodoList.addTask("Have breakfast");
myTodoList.addTask("Go to work");
myTodoList.display();
myTodoList.remove(2);
myTodoList.display();
}
}
The output unsurprisingly shows your list without the breakfast task. Success! 😁
Here is our Todo List for My morning routine: Task 0 is Wake up Task 1 is Shower Task 2 is Go to work
Great! With so much time on your hands, you can replace your shower with a bath. 🛀🏿 Use the rename method, which takes two arguments:
The index of the task you want to replace. As you can see in the previous output, the index of the
Shower
is 1.The new name for the task.
Here is the code:
public class MorningRoutine {
public static void main(String[] args)
{
TodoList myTodoList = new TodoList("My morning routine");
myTodoList.addTask("Wake up");
myTodoList.addTask("Shower");
myTodoList.addTask("Have breakfast");
myTodoList.addTask("Go to work");
myTodoList.display();
myTodoList.remove(2);
myTodoList.display();
myTodoList.rename(1, "Take bath");
myTodoList.display();
}
}
Let's check the final output:
Here is our Todo List for My morning routine: Task 0 is Wake up Task 1 is Take bath Task 2 is Go to work
Now that you have seen how the logic workflow of your program is implemented in Java, let's see how the class is actually implemented.
Define the class name and its fields
In order to implement this design, you must define in code:
The name of the class, which should clearly communicate that you are creating a model for a to-do list. Use
TodoList
, which follows the official Java naming conventions.Attributes (fields) need to store the state of the class. As you saw in the first chapter, you need to store the topic and the list of tasks.
The methods you called in your workflow: add, remove, rename, display.
Start by defining the class and its fields. Implement each operation as methods of the class. Here is the code for the definition of the name and the fields of the TodoList
class:
/** TodoList models the attributes
* and the operations we can perform
*/
public class TodoList {
private String topic;
private ArrayList<String> tasks;
//...
}
The code starts with a documentation comment, allowing you to generate a Javadoc. After the Javadoc, the following code public class TodoList
defines:
The accessibility level:
public
The class name:
TodoList
These are followed by the opening curly brace{
which marks the beginning of the class body. The body starts with the two fields of the class that correspond to the attributes defined in the last chapter:
String topic;
is the field that stores the reason why the class is used.ArrayList<String> tasks;
stores the actual class as a list of strings.
You can now define the different operations that the objects, created from your custom to-do list datatype, will be able to perform.
Implement the object creation method
In the main
method of the program, you used the following statement to create the object:
TodoList myTodoList = new TodoList("My morning routine");
We mentioned earlier that the "My morning routine" string is assigned to the topic
field. In Java, this is done by defining a constructor in the class body. The role of the constructor is to initialize the fields when an object is created. Here is the code located between the opening and closing curly braces of the class:
/** TodoList instantiates a TodoList with the provided string as a topic for the list
* and allocates the memory for the list of tasks
* @param topic The topic for which the list is created for
*/
public TodoList(String topic) {
this.topic=topic;
this.tasks=new ArrayList<String>();
}
Let's break this up!
public TodoList(String topic)
is the signature of the constructor method.public
means the constructor is accessible.TodoList
is the name of the constructor. As you can see, a constructor bears the same name as the class. This is a requirement!String topic
is the parameter of the class which is needed to initialize thetopic
field.
There are two statements inside the constructor method:
this.topic=topic;
assigns the value of thetopic
parameter into thetopic
field.this.tasks=new ArrayList<String>();
allocates the memory for the list. Failure to do so will cause the program to crash when trying to use the list.
How does Java differentiate between topic as a field, and topic as the local variable defined as a parameter?
Even though the field and the local variable bear the same name, the field is prefixed with the this
keyword and the .
operator.
this
is actually a reference to the object that will be created from the class.The
.
operator means that you access the element to its right belonging to the element to its left.
Therefore, the .
between this
and tasks
means that you are "accessing the tasks
element of the this
reference."
Implement the addTask method
Let's implement your first method that will enable the program to add a task to your list. Here is the Java code for your addTask
method:
/** addTask appends the provided String at the end of the list
* @param taskName The description of the task to be added
*/
public void addTask(String taskName) {
this.tasks.add(taskName);
}
The important thing to notice here is that the addTask
method only needs one argument: the string to be added. Since the actual list is stored as a field named tasks
, it is directly accessible from the method.
Let's look more closely at the one line of code inside the body of the method: this.tasks.add(task);
this
is the reference to the object that will be created from the class.tasks
is the field that stores the tasks as a list.add
is a method provided by theArrayList
class that adds the value of thetask
variable at the end of the list.
As you saw in the constructor case, this.tasks.add(task);
really means "call the add
method of the tasks
field of the this
reference."
You now understand why the myTodoList.addTask("Wake up");
statement added the first task of the day to your to-do list in the main method of your program. Let's now see how the second method, display()
, is implemented.
Implement the display method
In order to visualize the content of a list, it is necessary to browse through all the elements of the list and display them, element by element. Here is the code:
/** display displays the topic of the list
* and each task order and description
*/
public void display() {
if(this.tasks.size()==0){
System.out.println("Our Todo List for " + topic + "is currently empty!");
}else {
System.out.println("Here is our Todo List for " + topic);
for(int i=0;i<this.tasks.size();i++) {
System.out.println("Task " + i + " is " + this.tasks.get(i));
}
}
}
Let's break this down:
Since the method is public, start with a Javadoc comment between
/**
and*/
The signature of the method is
public void display()
. It does not define parameters as all its work will be performed in the fields.Inside the method, start by testing if the list is empty with
if(this.tasks.size()==0){
If it is, only display the topic of the to-do list:
System.out.println("Our Todo List for " + topic + "is currently empty!");
Else, you loop through the list to print its content item by item:
System.out.println("Here is our Todo List for " + topic);
for(int i=0;i<this.tasks.size();i++) {
System.out.println("Task " + i + " is " + this.tasks.get(i));
}
That's it! Do not hesitate to take another look at the chapter on loops if you're not entirely clear.
Did you notice, when you studied your main
method at the beginning of this chapter, that the list for your example started with a weird "Task 0" as the first task ? To refresh your memory, here is what we printed after adding the four tasks.
Here is our Todo List for My morning routine: Task 0 is Wake up Task 1 is Shower Task 2 is Have breakfast Task 3 is Go to work
Now you have two operations implemented for your class: add, and display. Let's now move on to the removeTask
method.
Implement the removeTask method
If you were working with an array instead of a list, you would have to write complex code to shift the items located after the item to be removed in order to overwrite it.
Thankfully, the ArrayList collection lets you do this in one line!
Here is the code:
/** remove removes a task at the provided index
* @param i The index of the task to remove
*/
public void removeTask(int i) {
try {
this.tasks.remove(i);
}
catch (IndexOutOfBoundsException e) {
System.out.println("Could not remove task, no task at this index!");
}
}
The basic structure is the same as the previous method: a Javadoc comment, public
accessibility and a meaningful name. This time we do have a parameter: the index of the element to be removed.
Here are two remarkable features:
We are using the
remove
method, as defined in the ArrayList documentation.The
tasks.remove(i);
statement is part of atry .. catch
block. This is because the remove method will throw an exception if we provide an index outside the range of existing values. Catching that exception allows your program to continue by simply printing a message instead of crashing.
Let's complete our class implementation with our final method: rename
.
Implement the rename operation
The renaming operation is straightforward. Simply provide the position of the task to be renamed and its new value. As in the previous method, you will be relying on a method provided by the ArrayList class.
Here's the code:
/** rename renames the task at the provided index
*
* @param i The index of the task to rename
* @param newtask The new description for the task
*/
public void rename(int i, String newTask) {
try {
this.tasks.set(i,newTask);
}
catch (IndexOutOfBoundsException e) {
System.out.println("Could not rename task, no task at this index!");
}
}
Compared to the removeTask
method, you'll notice two main differences:
You need two parameters: the index of the task to be renamed, and the new name.
You are now using the set method of the ArrayList class.
For both the removeTask
and the rename
methods, things get more complicated if you don't know the index. In that case, you need to search for the value by looping through the list. Once you have the index that contains the value, you can call the methods defined above.
Summary
In this chapter, you have created a well-documented class that can be used in any project where you may need a to-do list.
Throughout this chapter, you have seen that object-oriented programming in general and Java programming in particular really consists of implementing a two-level thought process:
Implement the capabilities you need as blueprints named classes that implement the needed operations as methods. Those methods operate on fields that are variables that define the state of the class.
Implement the logic of your program by creating instances of classes called objects. Things happen by sending messages to objects.
In the next chapter, we will enhance what your to-do list can do by allowing you to mark a task as completed, learning some cool new things along the way.