Skip to content

[Bug] InaccessibleObjectException with generic enum fields in EqualsVerifier 4.x #1122

@olegvelikanov

Description

@olegvelikanov

Describe the bug

I've encountered an issue where EqualsVerifier 4.1 throws java.lang.reflect.InaccessibleObjectException when verifying classes with generic enum fields (E extends Enum<E>). This affects both direct generic enum containers and classes that inherit generic enum fields from superclasses.
This works correctly in EqualsVerifier 3.x but seems to fail in all 4.x versions I've tested.

Steps to reproduce

  1. Create a class with a generic enum field E extends Enum<E>, or
  2. Create a class that extends a superclass with a generic enum field
  3. Run EqualsVerifier on the class
  4. The test fails with InaccessibleObjectException

Error message and version number

EqualsVerifier Version: 4.1
Java Version: 21.0.5

EqualsVerifier found a problem in class com.example.Dummy.
-> Field state of type java.lang.Enum is not accessible via the Java Module System.
Consider opening the module that contains it, or add prefab values for type java.lang.Enum.

java.lang.AssertionError
Caused by: nl.jqno.equalsverifier.internal.exceptions.ModuleException
Caused by: nl.jqno.equalsverifier.internal.exceptions.ModuleException
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.lang.String java.lang.Enum.name accessible: module java.base does not "opens java.lang" to unnamed module

Code: EqualsVerifier invocation

import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test;

class DummyTest {
    @Test
    void has_correct_equals_and_hashcode() {
        EqualsVerifier.forClass(Dummy.class)
            .usingGetClass()
            .verify();
    }
}

Code: class under test

import java.util.Objects;

// Base class with generic enum field
class StatefulEntity<S extends Enum<S>> {
    
    public final S state;

    protected StatefulEntity(S state) {
        this.state = state;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        StatefulEntity<?, ?> statefulEntity = (StatefulEntity<?, ?>) o;
        return Objects.equals(state, statefulEntity.state);
    }

    @Override
    public int hashCode() {
        return Objects.hash(state);
    }
}

// Concrete implementation
class Dummy extends StatefulEntity<Dummy.State> {
    private final String value;

    public Dummy(State state, String value) {
        super(state);
        this.value = value;
    }

    @Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) return false;
        if (!super.equals(o)) return false;
        Dummy someModel = (Dummy) o;
        return Objects.equals(value, someModel.value);
    }

    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), value);
    }

    public enum State {
        A, B
    }
}

Additional context

This appears to be a regression introduced in EqualsVerifier 4.0, as the same code works correctly with version 3.19.4.

Thank you for your time and consideration in looking into this issue!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions