Table of Contents
Introduction
When inheriting method logic from a parent, we may need to alter that logic in the child. When we do so, this is called overriding. The method is still there, we are just providing our own version of the logic within that method call. To demonstrate this behavior, let’s update the behavior in our Animal class to override the toString() method from the Object (implicit) parent class. This is allowed in most cases. The exceptions to this rule are a topic for another post.
Diagram
This diagram is much smaller than the last post, so not as worried about folks having to scroll past it.

Unit Test for Object’s toString() Implementation
Let’s start by creating a unit test that demonstrates what the Object class’s toString() logic generates. Let’s add the following to the src/test/java/codingchica.java101.model.AnimalTest.java:
/**
* Unit tests for the toString method.
* @see Animal#toString()
*/
@Nested
class ToStringTest {
@Test
void toString_whenInvoked_returnsExpectedFormat(){
// Setup
String expectedStringPrefix = "codingchica.java101.model.Animal@";
// Execution
String result = animal.toString();
// Validation
assertNotNull(result, "result not null");
assertTrue(result.startsWith(expectedStringPrefix),
() -> String.format("Expected '%s' to start with '%s'",
result,
expectedStringPrefix));
}
}
This test uses a startWith check because what follows this prefix is a representation of the object’s location in memory and will change with each execution.
Normally, I wouldn’t bother adding a test for this inherited behavior, unless I want to confirm that it stays that way. However, in this case, the extra step in the unit test helps to demonstrate that the behavior does already exist and is changing, rather than being added, with our runtime updates.
TDD Cycle
Now that we have confirmed that the inherited toString() implementation is used by our class, let’s modify that behavior.
Unit Tests
First, let’s update our unit tests to confirm the expected behavior after our updates.
/**
* Unit tests for the toString method.
* @see Animal#toString()
*/
@Nested
class ToStringTest {
@ParameterizedTest
@EnumSource(value = RelativeTime.class, mode = EnumSource.Mode.EXCLUDE, names = {})
void toString_whenTimeOfBirthSet_returnsExpectedFormat(RelativeTime dateTimeValue){
// Setup
String expectedString = String.format("Animal{timeOfBirth=%s}", dateTimeValue.getInstant());
animal.setTimeOfBirth(dateTimeValue.getInstant());
// Execution
String result = animal.toString();
// Validation
assertNotNull(result, "result not null");
assertEquals(expectedString, result, "result");
}
@Test
void toString_whenTimeOfBirthNotSet_returnsExpectedFormat(){
// Setup
String expectedString = "Animal{timeOfBirth=null}";
// Execution
String result = animal.toString();
// Validation
assertNotNull(result, "result not null");
assertEquals(expectedString, result, "result");
}
}
In the code snippet above, we have updated the existing test to show that we expect a shorter class name, and the timeOfBirth field to be outputted, but not the object reference information. This test has been updated to use our RelativeTime enum as a data source, so we can confirm that the value in the output is not simply hard-coded. We have also added a new test to confirm that the default value of null is shown if the field is not set.
Runtime Updates
Now, let’s add the logic to override the existing toString() method logic:
/**
* A string representation of the Animal class.
*
* @return A string representing the data in this instance of the Animal
* class.
*/
@Override
public String toString() {
// TODO improve the performance of this code once we discuss the
// better approaches.
return "Animal{"
+ "timeOfBirth="
+ timeOfBirth
+ '}';
}
In this example, we are using String concatenation (the + symbol) to join multiple strings together. This works in our local unit tests, but it comes at a performance cost and is considered a bad practice. We briefly looked at String.format in a prior post (when adding the App getGreeting() method). However, since we have not yet discussed the reason some alternatives are better approaches, I left the String concatenation in place for the time being and added a TODO comment – a little note to ourselves for the future that we need to circle back to this performance concern.
You may also notice the @Override annotation. This is for our benefit – if we ever change either the parent or the child class’s code so that we are no longer overriding a method from our parent, but rather implementing an entirely new method, then the code will not compile. You can try this out by temporarily adding an extra character to the method name, or adding an input parameter. Either change will cause the code to no longer compile, as we would not be overriding a method from our parent.
Maven Build
At this point, if we run the Maven build, we should see success.
Commit
It is good to commit in small, incremental changes. Now is a good time to do so.

Leave a comment