Lab 18 10 pts Week 8 Due May 22

Lab 18: Student Class

Fields, constructors, encapsulation, toString, equals, and testing

classes constructors encapsulation toString equals junit
Clone from GitHub
3 checkpoints 5 autograder 2 timeliness
Prerequisites: lesson-4-1 lesson-4-2 lesson-4-3 lesson-4-4

After this lab, you will be able to:

  • Define a class with private fields, a constructor, and getter methods
  • Override toString to produce a formatted string representation
  • Override equals and hashCode for value-based equality
  • Write JUnit tests that verify constructor behavior, equality, and edge cases

What You’re Building

You will design a Student class from scratch: private fields for name, ID, and GPA; a constructor that initializes all three; getters (no setters — immutable design); a toString override; and an equals/hashCode pair that compares students by ID. Then you will write JUnit tests that verify your class behaves correctly. This is a full-week lab because it covers the core OOP skill set you will use for the rest of the quarter.

Timeline: This lab is assigned Monday and due Friday. Start early — the testing checkpoint takes time to think through.


Concepts and Misconceptions

Concept Common Mistake What the Test Catches
Private fields Declaring fields as public, bypassing encapsulation Test accesses fields via getters only; public fields are flagged by style check
Constructor Assigning parameter to itself (name = name; instead of this.name = name;) Getter returns null after construction
toString Returning null or printing inside toString instead of returning a String Test calls toString() and compares the returned value
equals Comparing with == instead of overriding equals, or forgetting the Object parameter type Two students with the same ID are not considered equal
hashCode Overriding equals without overriding hashCode Autograder checks that equal objects produce equal hash codes
JUnit Writing tests that always pass (e.g., assertTrue(true)) instead of testing real behavior Test quality check verifies assertions reference your Student methods

Checkpoints

Checkpoint 1: Private Fields, Constructor, Getters, toString

What to do: Create a Student class with three private fields: String name, int id, and double gpa. Write a constructor that takes all three as parameters and assigns them using this. Write getter methods for each field. Override toString to return a formatted string like "Student{name='Ada', id=12345, gpa=3.95}".

What the test checks: Constructing a Student and calling each getter returns the correct value. Calling toString returns the expected format.

Debugging tip: If getters return null or 0, you probably wrote name = name; in the constructor. The parameter shadows the field. Use this.name = name; to disambiguate. If toString fails, check the exact format — spaces, quotes, commas, and braces all matter.

Checkpoint 2: equals and hashCode

What to do: Override equals(Object obj) so two Student objects are equal if they have the same id. Follow the standard pattern: check for null, check getClass(), cast, and compare the id field. Override hashCode to return Integer.hashCode(id) so that equal objects always produce the same hash code.

What the test checks: Two students with the same ID are .equals(), two with different IDs are not. A student is not equal to null or a non-Student object. Equal students have equal hash codes.

Debugging tip: If equals always returns false, check that your parameter type is Object, not Student. Writing equals(Student other) is overloading, not overriding — the test calls equals(Object). If the null check fails, make sure you handle null before casting.

Checkpoint 3: Write 3 JUnit Tests

What to do: In the test file, write at least three meaningful JUnit tests. Test (a) that the constructor and getters work correctly, (b) that two students with the same ID are equal and two with different IDs are not, and (c) an edge case such as toString format or equality with null.

What the test checks: Your test file compiles, all your tests pass, and each test contains assertions that call Student methods.

Debugging tip: If your tests compile but do not run, check that your test methods are annotated with @Test and that you imported org.junit.jupiter.api.Test and static org.junit.jupiter.api.Assertions.*. If a test fails, fix your Student class — the goal is to write tests that catch real bugs, so a failing test means your implementation has an issue.


How to Debug

  1. Test one method at a time. Write the constructor and getters first, then run the checkpoint 1 tests. Do not move to equals until the basics work. Layering code on a broken foundation wastes time.

  2. Use System.out.println in main. Before running JUnit, create a few Student objects in a main method and print them. Verify toString output visually. Call equals and print the result. This quick check catches obvious issues before the test framework is involved.

  3. Read the equals contract. equals must be reflexive (a.equals(a)), symmetric (a.equals(b) == b.equals(a)), and handle null (return false). If any of these fail, check your implementation against the standard pattern shown in lecture.


Scoring

Component Points Criteria
Checkpoints 3 1 pt each. Binary: the checkpoint test passes or it does not.
Autograder 5 Correctness across all test cases. Partial credit by proportion of tests passed.
Timeliness 2 Full credit if submitted by the due date. 0 if late.
Total 10