Backend Development 11 min read

Resolving JaCoCo-Induced ClassCastException Caused by Synthetic $jacocoData Field in Java Applications

This article details a Java ClassCastException caused by JaCoCo’s injected $jacocoData boolean array, explains how synthetic fields arise during bytecode instrumentation, analyzes the failing encryptObject method, and presents a fix by filtering synthetic fields using isSynthetic() to restore normal order processing.

JD Tech
JD Tech
JD Tech
Resolving JaCoCo-Induced ClassCastException Caused by Synthetic $jacocoData Field in Java Applications

After integrating the JaCoCo code‑coverage tool into a Java backend service, the order‑processing flow started to fail with a java.lang.ClassCastException: [Z cannot be cast to [Ljava.lang.Object; stack trace, indicating a type‑conversion problem during encryption of request fields.

The error originates from JaCoCo inserting a synthetic boolean array field named $jacocoData into every instrumented class. In Java, the internal representation of a boolean array is "[Z", which explains the cryptic exception message.

The problematic code is the encryptObject method, which recursively processes objects. When it encounters an array, it casts the object to (Object[]) and iterates over it, leading to the ClassCastException for the synthetic boolean array:

public void encryptObject(Object obj, String type) throws IllegalAccessException {
    // ...
    else if (clazz.isArray()) {
        // ------------------- error line -------------------
        for (Object o : (Object[]) obj) {
            // ------------------- error line -------------------
            this.encryptObject(o, type);
        }
    }
    // ...
}

The method getDeclaredFieldsAll(Class clazz) collects all fields of a class (including those from super‑classes) via reflection, which now also returns the synthetic $jacocoData field.

public Field[] getDeclaredFieldsAll(Class clazz) {
    List
fieldsList = new ArrayList<>();
    while (clazz != null) {
        Field[] declaredFields = clazz.getDeclaredFields();
        fieldsList.addAll(Arrays.asList(declaredFields));
        clazz = clazz.getSuperclass();
    }
    return fieldsList.toArray(new Field[fieldsList.size()]);
}

Java compilers generate synthetic members (fields or methods) to support features such as inner‑class access. For example, the compiler creates an access$100 method for a private field of an inner class, and JaCoCo adds the synthetic $jacocoData boolean array for probe storage.

JaCoCo works by inserting probe booleans (using ASM) into the bytecode; each probe records whether a particular instruction has been executed. The probes are stored in the synthetic $jacocoData array, which does not affect program logic but becomes visible to reflection‑based utilities.

To prevent the synthetic field from breaking the encryption logic, the field list is filtered to exclude synthetic members before further processing:

List
fieldsList = Arrays.stream(declaredFields)
    .filter(field -> !field.isSynthetic())
    .collect(Collectors.toList());

After applying this filter and redeploying the service with JaCoCo configuration, the order‑processing flow works correctly, confirming that the $jacocoData field was the root cause.

In summary, when using bytecode instrumentation tools like JaCoCo, developers should be aware of compiler‑generated synthetic fields and ensure reflection‑based utilities handle or ignore them to maintain robust backend functionality.

debuggingJavaReflectionJaCoCoClassCastExceptionSyntheticFields
JD Tech
Written by

JD Tech

Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.