:pencil: Today I Wrote

A java function that lists an object's field names

programming Java java reflection

In order to make some assertions in a Java project’s automated test suite, I needed to get the list of private, protected and public members of an object.

Using Class#getDeclaredFieldNames(), you can easily get a class’s Fields, an example of reflection in Java:

Class<?> someClass = someObject.getClass();
Field[] declaredFields = someClass.getDeclaredFields();

For our purposes, we’ll get that as a stream:

Stream<Field> declaredFields = Arrays.stream(
  someClass.getDeclaredFields()
);

Unfortunately, this only returns the class’s direct fields. It doesn’t include fields from parent classes. But we can do that with a recursive function:

package com.alphahydrae.example;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.stream.Stream;

public final class FieldUtils {

  public static Stream<Field> streamFields(
    Class<?> currentClass
  ) {
    // The stop condition for the recursive function: when we
    // run out of parent classes, return an empty stream.
    if (currentClass == null) {
      return Stream.empty();
    }

    // Return the stream of the parent class's fields
    // concatenated to the current class's.
    return Stream.concat(
      streamFields(currentClass.getSuperclass()),
      Arrays.stream(currentClass.getDeclaredFields())
    );
  }

  private FieldUtils() {}
}

Using the functional powers granted to us by streams, we can easily:

  • Filter out the fields we don’t want (in this case I did not want static fields to be listed);
  • Get the names of the fields.
// ...
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.stream.Collectors;

public final class FieldUtils {
  // ...

  public static List<String> getDeclaredFieldNames(
    Object object
  ) {
    return getFields(object.getClass())
      // Filter out static fields.
      .filter(field ->
        !Modifier.isStatic(field.getModifiers()))
      // Get the names.
      .map(Field::getName)
      // Collect them into a list.
      .collect(Collectors.toList());
  }

  // ...
}

Done.

Here’s how you could use it:

// PersonDto.java
public class PersonDto {
  public String firstName;
  public String lastName;
}

// EmployeeDto.java
public class EmployeeDto extends PersonDto {
  public String employeeNo;
}

// EmployeeDtoTests.java
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import org.junit.jupiter.api.Test;

public class EmployeeDtoTests {

  @Test
  void employeeDtoFieldsHaveNotChanged() {
    assertThat(
      FieldUtils.getDeclaredFieldNames(new EmployeeDto()),
      containsInAnyOrder(
        "firstName",
        "lastName",
        "employeeNo"
      )
    );
  }
}