Day 3: Java Deep Dive Exercise
This exercise combines the concepts of generics, lambda expressions, streams, and dates. The exercise involves creating a simple task list application using Java. You’ll need to implement classes to manage tasks, create a task list, and perform operations like filtering, sorting, and date-based calculations.
Exercise: Task List Application
Create a Java program that simulates a task list application. Follow these steps:
-
Create the Task Class:
Create a
Taskclass with the following attributes:title(String): The title of the task.description(String): The description of the task.dueDate(LocalDate): The due date of the task.
-
Create a GardenTask Class:
Create a
GardenTaskclass that extends theTaskclass. TheGardenTaskclass must have an additional attribute calledgardenLocation(String) that stores the location of the garden where the task needs to be performed. -
Create the TaskList Class:
Create a
TaskListclass that manages a collection of Tasks (and GardenTasks). Include methods to:- Add tasks to the list.
- Filter tasks based on a keyword in the title or description.
- Sort tasks by due date.
- Get tasks that are due today.
- Get tasks that are overdue.
- Print the list of tasks.
The class must be generic and accept any Task or subtype of Task. The class must also implement the
Iterableinterface to allow iterating over the tasks. -
Main Application:
In the main application:
- Create instances of
TaskandGardenTaskand add them to aTaskList. - Demonstrate the usage of lambda expressions and streams for filtering and sorting tasks.
- Use the Java Time API to set due dates and perform date-based calculations.
- Print the resulting task lists after each operation.
- Create instances of
This exercise will help you practice the following skills:
- Defining and using classes with generics (
Task,GardenTaskandTaskList). - Using lambda expressions for filtering and sorting (
TaskListmethods). - Utilizing streams to process collections (
TaskListmethods). - Working with the Java Time API for handling dates (
TaskdueDate). - Implementing various methods to perform operations on collections (
TaskListmethods).
- Background
Using generics for your Task and TaskList classes can offer several benefits in terms of type safety, reusability, and flexibility. Let’s explore the advantages of using generics in each of these classes:
1. Type Safety:
Using generics ensures that you can define the specific type of data each class will work with. This helps catch type-related errors at compile time rather than runtime, improving the overall reliability of your code. For example, by using generics, you can ensure that only Task objects are added to the TaskList, and any attempt to add other types of objects will result in a compile-time error.
2. Reusability:
Generics allow you to create more versatile and reusable classes. By parameterizing the classes with a type, you enable them to work with different data types while maintaining the same structure and behavior. This can make your code more adaptable to changes and easier to extend in the future.
3. Flexibility:
Using generics enables you to create classes that can work with different types without the need for duplicating code. For instance, you might want to create different TaskList instances that store tasks of different types, such as personal tasks and work-related tasks. With generics, you can achieve this flexibility without having to create separate classes for each type of task list.
4. Avoiding Type Casting:
Generics help eliminate the need for explicit type casting when retrieving objects from collections. In your TaskList class, when you use a generic type parameter for the task list, you won’t need to cast elements to the appropriate type when retrieving them from the list.
Here’s an example of how you might use generics in your Task and TaskList classes:
import java.time.LocalDate;
class Task {
private String title;
private String description;
private LocalDate dueDate;
public Task(String title, String description, LocalDate dueDate) {
this.title = title;
this.description = description;
this.dueDate = dueDate;
}
// ... getters and setters
}
class TaskList<T extends Task> {
private List<T> tasks;
public TaskList() {
tasks = new ArrayList<>();
}
public void addTask(T task) {
tasks.add(task);
}
public List<T> getTasks() {
return tasks;
}
// ... other methods
}
In this example, the TaskList class is parameterized with a type T that extends the Task class. This ensures that the TaskList can only work with subclasses of Task, providing type safety and better organization of your code.
By using generics in your Task and TaskList classes, you can create more robust and flexible code that adheres to Java’s type system and promotes cleaner, safer, and more reusable code.