Skip to content

Conversation

@inponomarev
Copy link
Contributor

This issue is tracked in https://issues.apache.org/jira/browse/LANG-1818

Problem

ClassUtils.getShortClassName(Class) currently delegates to the string-based getShortClassName(String) using cls.getName().

The string-based method is inherently heuristic and, as documented, cannot reliably distinguish package names, outer classes, and inner classes when given only a JVM binary name. As a result, any $ character is treated as an inner-class separator, even when $ is part of a legitimate Java identifier.

While this limitation is unavoidable for getShortClassName(String), it should not apply to getShortClassName(Class), where full reflective metadata is available.

Changes

  • Reimplemented getShortClassName(Class) (and by extension getShortClassName(Object)) to use Class metadata instead of parsing Class.getName().
  • Correctly preserves $ characters that are part of actual class identifiers (top-level classes, member classes, and nested member classes).
  • Maintains existing behavior for local and anonymous classes by falling back to the string-based logic, preserving compiler-generated ordinal naming expected by existing tests.
  • Leaves getShortClassName(String) and getShortCanonicalName(String) unchanged, along with their documented limitations.

Examples (fixed behaviour)

class $trange {}
class Pa$$word {}

class Outer {
    class $Inner {}
    class Inner {
        class Ne$ted {}
    }
}

ClassUtils.getShortClassName($trange.class)        // "$trange"
ClassUtils.getShortClassName(Pa$$word.class)       // "Pa$$word"
ClassUtils.getShortClassName(Outer.$Inner.class)   // "Outer.$Inner"
ClassUtils.getShortClassName(Outer.Inner.Ne$ted.class) // "Outer.Inner.Ne$ted"

Rationale

The ambiguity of $ in JVM binary names is explicitly documented for the string-based APIs. However, when a Class<?> instance is available, that ambiguity disappears. This change improves correctness for the Class-based overloads without altering or reinterpreting the behavior of the string-based methods.

Copy link
Member

@garydgregory garydgregory left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @inponomarev

Thank you for the PR. I have a few comments here and there.


@Test
void testDollarSignImmediatelyAfterPackage() {
String result = ClassUtils.getShortClassName($trange.class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline result.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed -- inlined result everywhere

dim++;
c = c.getComponentType();
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the extra blank line. If you want to "phrase" a method, you can use a // comment to explain what's happening.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

final StringBuilder sb = new StringBuilder(base);
for (int i = 0; i < dim; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could reuse StringUtils.repeat() or AppendableJoiner.join(StringBuilder, T...) or at least pre-allocate the StringBuilder since its final size is known.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, indeed, why not StringUtils.repeat()?

@garydgregory garydgregory changed the title LANG-1818: Fix ClassUtils.getShortClassName(Class) to correctly handle $ in legitimate class names LANG-1818: Fix ClassUtils.getShortClassName(Class) to correctly handle $ in valid class names Jan 28, 2026
@garydgregory garydgregory merged commit 4052e98 into apache:master Jan 28, 2026
20 checks passed
garydgregory added a commit that referenced this pull request Jan 28, 2026
$ in valid class names #1591

- Javadoc
- Sort class name
- Sort new members
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants