Skip to content

Retrieve the no-argument constructor#1690

Merged
kingthorin merged 3 commits intodatafaker-net:mainfrom
mt657-jpg:1689-no-arg-constructor-issue
Oct 6, 2025
Merged

Retrieve the no-argument constructor#1690
kingthorin merged 3 commits intodatafaker-net:mainfrom
mt657-jpg:1689-no-arg-constructor-issue

Conversation

@mt657-jpg
Copy link
Copy Markdown
Contributor

@mt657-jpg mt657-jpg commented Oct 1, 2025

…using a method called getParameterlessPublicConstructor.

This fixes #1689 where the no-argument constructor is not always the one you get

…erlessPublicConstructor

This fixes issue datafaker-net#1689 where the no-argument constructor is not always the one you get
@asolntsev asolntsev changed the title Retrieve the no-argument constructor using a method called getParamet… Retrieve the no-argument constructor Oct 1, 2025
@asolntsev asolntsev added this to the 2.5.2 milestone Oct 1, 2025
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Oct 1, 2025

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 73.33333% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.30%. Comparing base (73291a1) to head (d040af6).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...tafaker/transformations/JavaObjectTransformer.java 73.33% 2 Missing and 2 partials ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #1690      +/-   ##
============================================
+ Coverage     92.28%   92.30%   +0.02%     
- Complexity     3410     3416       +6     
============================================
  Files           334      334              
  Lines          6752     6760       +8     
  Branches        670      671       +1     
============================================
+ Hits           6231     6240       +9     
+ Misses          355      354       -1     
  Partials        166      166              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@kingthorin kingthorin requested a review from Copilot October 1, 2025 20:17
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR ensures that object instantiation uses the parameterless public constructor when available, fixing cases where an arbitrary constructor was previously selected.

  • Replace selection of the first declared constructor with a lookup for the parameterless public constructor.
  • Add a helper method getParameterlessPublicConstructor and refactor hasParameterlessPublicConstructor to use it.
  • Throw a RuntimeException when no parameterless public constructor exists.

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +178 to +180
return Arrays.stream(clazz.getConstructors())
.filter(constructor -> constructor.getParameterCount() == 0)
.findFirst();
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

Use the dedicated reflection API to retrieve the public no-arg constructor directly: clazz.getConstructor() is clearer and avoids array streaming. Suggested implementation:
private Optional<Constructor> getParameterlessPublicConstructor(Class clazz) {
try {
return Optional.of(clazz.getConstructor());
} catch (NoSuchMethodException e) {
return Optional.empty();
}
}

Suggested change
return Arrays.stream(clazz.getConstructors())
.filter(constructor -> constructor.getParameterCount() == 0)
.findFirst();
try {
return Optional.of(clazz.getConstructor());
} catch (NoSuchMethodException e) {
return Optional.empty();
}

Copilot uses AI. Check for mistakes.
if (primaryConstructor != null) {
result = primaryConstructor.newInstance();
} else {
throw new RuntimeException("Failed to locate a parameterless public constructor for class " + clazz.getName());
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

Throwing a generic RuntimeException obscures intent; consider using IllegalStateException (or wrapping NoSuchMethodException if switching to clazz.getConstructor()) to more precisely convey the failure to obtain a required public no-arg constructor.

Suggested change
throw new RuntimeException("Failed to locate a parameterless public constructor for class " + clazz.getName());
throw new IllegalStateException("Failed to locate a parameterless public constructor for class " + clazz.getName());

Copilot uses AI. Check for mistakes.
try {
Constructor<?> primaryConstructor = CLASS2CONSTRUCTOR.computeIfAbsent(clazz, c -> c.getDeclaredConstructors()[0]);
result = primaryConstructor.newInstance();
Constructor<?> primaryConstructor = CLASS2CONSTRUCTOR.computeIfAbsent(clazz, c -> getParameterlessPublicConstructor(c).orElse(null));
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

Returning null from computeIfAbsent means classes without a public no-arg constructor are never cached (causing repeated lookups). Consider caching the negative result, e.g., by changing the cache to Map<Class, Optional>> and storing Optional.empty(), or by using a separate negative cache.

Copilot uses AI. Check for mistakes.
@bodiam
Copy link
Copy Markdown
Contributor

bodiam commented Oct 3, 2025

Would it be possible to add a few testcases which demonstrate some scenarios? (like only a no arg (private|package|public) constructor, only a constructor with parameters, a class with both a no-arg and parameter constructor, etc?)

Add a test to cover the scenarios where the no-arg constructor is public, protected and private

Improve the code slightly that finds tries to find an alternative constructor. This could be improved further to find one that matches the schema. Currently it just finds the first public constructor in the array returned by clazz.getConstructors()
@mt657-jpg
Copy link
Copy Markdown
Contributor Author

I have added some tests and filtered for public constructors only

@bodiam
Copy link
Copy Markdown
Contributor

bodiam commented Oct 6, 2025

Looks great. If you're happy with it, happy to merge it! Thanks for your work on this!

@kingthorin kingthorin merged commit f7f7651 into datafaker-net:main Oct 6, 2025
13 checks passed
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.

The JavaObjectTransformer sometimes finds a constructor other than the no-argument one when it is intending to use the no-argument constructor

7 participants