Due: Thursday, September 25 at 11:59 PM Points: 100 Submission: Via GitHub (one per team)
After mastering OOP principles in Assignments 1 and 2, you'll now apply functional programming concepts in Java. This assignment focuses on streams, lambda expressions, Optional, method references, and functional interfaces to process and analyze task data.
- Master Java Stream API for data processing
- Use Optional for null-safe operations
- Apply lambda expressions and method references effectively
- Create and use custom functional interfaces
- Implement functional composition and higher-order functions
- Understand default and static methods in interfaces
You're provided with a task management system that needs its core processing logic implemented using functional programming techniques. The domain model is complete, but all the functional operations need to be implemented.
Implement all TODO methods in TaskAnalyzer
using streams and functional programming:
filterTasks(Predicate<Task>)
- Filter using stream().filter()getTaskTitles()
- Map tasks to titles using method referencesgetTopPriorityTasks(int)
- Sort by priority and limit results
groupByStatus()
- Use Collectors.groupingBy()partitionByOverdue()
- Use Collectors.partitioningBy()countTasksByPriority()
- Group and count with collectors
getTotalEstimatedHours()
- Use reduce() or mapToInt().sum()getAverageEstimatedHours()
- Calculate average with streamsgetAllUniqueTags()
- Collect unique values with flatMap()
hasOverdueTasks()
- Use anyMatch()areAllTasksAssigned()
- Use allMatch() or noneMatch()
Master the Optional API for null-safe operations:
findTaskById(Long)
- Return OptionalgetTotalEstimatedHours()
- Handle null hours with OptionalgetTaskSummary(Long)
- Chain Optional operations with map() and orElse()
Example Optional chaining:
return tasks.stream()
.filter(t -> t.id().equals(id))
.findFirst()
.map(task -> String.format("%s - %s", task.title(), task.status()))
.orElse("Task not found");
Extends Predicate<Task>
with default methods:
and(TaskPredicate)
- Combine predicatesor(TaskPredicate)
- Alternative conditionsnegate()
- Invert predicate- Static factory methods for common filters
Extends Function<Task, Task>
for transformations:
andThen(TaskTransformer)
- Compose transformations- Static factory methods for common transformations
Custom interface for batch processing:
process(List<Task>)
- Process task collectionsandThen(TaskProcessor)
- Chain processors
Implement advanced functional programming patterns:
processPipeline()
- Chain multiple operationsfilterAndTransform()
- Combine predicates and transformers
transformAll()
- Apply UnaryOperator to all tasksprocessTasksWithSideEffects()
- Use Consumer for side effects
getOrCreateDefault()
- Use Supplier for lazy evaluationgenerateTaskStream()
- Create infinite streams with generate()
mergeTasks()
- Combine two tasks using BiFunctionsortByMultipleCriteria()
- Compose multiple comparators
public record Task(
Long id,
String title,
String description,
Priority priority,
Status status,
Set<String> tags,
LocalDateTime createdAt,
LocalDateTime dueDate,
Integer estimatedHours
)
TaskAnalyzerTest
- 22 tests for stream operationsTaskProcessingEngineTest
- 14 tests for functional patterns
Component | Points | Requirements |
---|---|---|
Stream Operations | 40 | All TaskAnalyzer methods using streams correctly |
Optional Usage | 15 | Proper Optional handling and chaining |
Custom Interfaces | 25 | TaskPredicate, TaskTransformer, TaskProcessor implementation |
Higher-Order Functions | 20 | TaskProcessingEngine with composition and lambdas |
# Clone the assignment repository
git clone [assignment-repo-url]
cd assignment-3-functional
# Run tests to see what needs implementation
./gradlew test
-
Start with TaskAnalyzer (easier methods first):
filterTasks()
- Basic filter operationgetTaskTitles()
- Simple map operationfindTaskById()
- Introduction to Optional
-
Move to grouping operations:
groupByStatus()
- Collectors.groupingBy()getAllUniqueTags()
- flatMap() and collect()
-
Implement custom functional interfaces:
- Complete default methods in TaskPredicate
- Add static factory methods
-
Finish with TaskProcessingEngine:
- Start with simple transformations
- Build up to complex compositions
# Run all tests
./gradlew test
# Run specific test class
./gradlew test --tests TaskAnalyzerTest
# Run with detailed output
./gradlew test --info
return tasks.stream()
.filter(task -> task.priority() == Priority.HIGH)
.sorted(Comparator.comparing(Task::dueDate))
.map(Task::title)
.collect(Collectors.toList());
return findTaskById(id)
.filter(Task::isActive)
.map(Task::title)
.orElse("No active task found");
TaskPredicate urgent = TaskPredicate.byPriority(Priority.HIGH)
.or(TaskPredicate.isOverdue())
.and(TaskPredicate.isActive());
Function<List<Task>, List<Task>> pipeline =
filterByPriority
.andThen(sortByDueDate)
.andThen(limitToTop10);
❌ DON'T:
- Use traditional for loops when streams are more appropriate
- Ignore null checks - use Optional instead
- Modify state in lambda expressions
- Use get() on Optional without checking isPresent()
- Create unnecessarily complex stream pipelines
✅ DO:
- Use appropriate collectors (toList(), toSet(), groupingBy())
- Chain Optional operations instead of nested if statements
- Keep lambdas simple and readable
- Use method references where possible (Task::getTitle)
- Test edge cases (empty lists, null values)
Document your AI usage in a comment at the top of TaskAnalyzer.java
:
/**
* AI Collaboration Report:
* Tool: [ChatGPT/Claude/Copilot/Gemini]
*
* Most Helpful Prompts:
* 1. "How do I use Collectors.groupingBy with an enum?"
* 2. "Explain Optional.flatMap vs Optional.map"
*
* Concepts Learned:
* - [What you learned about functional programming]
*
* Team: [Member names]
*/
- All tests must pass:
./gradlew test
- Push completed code to GitHub
- Submit repository URL to Moodle
- Include team member names in submission
- Parallel Streams - Add parallel processing where beneficial
- Custom Collector - Create a custom Collector for task statistics
- Memoization - Implement caching for expensive operations
- Reactive Streams - Add CompletableFuture for async operations
- Performance Analysis - Compare stream vs loop performance
- Office hours: Wednesdays 1:30-3:00 PM
- Email: kkousen@trincoll.edu
Note: This assignment emphasizes functional thinking. Embrace immutability, pure functions, and declarative style. The tests are comprehensive - let them guide your implementation!