Skip to content

Conversation

robbytx
Copy link
Contributor

@robbytx robbytx commented Sep 22, 2020

This PR is equivalent to #102 but targets 2.11 instead. The fix (if you wish to cherry-pick separately) is in e0bfe3f.

Problem

When the type to materialize extends/implements some interface that has a default method implementation (with a non-bean method name), MrBean will treat the method as if it lacks an implementation.

Solution

Use Class.getMethod to locate the exact method that the implemented type will utilize, which properly handles this case.

Verification

I've added several unit tests to cover the fixed behavior, and to verify existing, related behavior:

  1. TestSimpleMaterializedInterfaces.testInheritedDefaultMethodInInterface is the test case that exercise the current issue, which is now resolved.
  2. TestSimpleMaterializedInterfaces.testDefaultMethodInInterface is a simpler test case that is already passing with the existing implementation.
  3. TestAbstractClasses and TestAbstractClassesWithOverrides have been extended to exercise some cases that justify the original code in hasConcreteOverride. These test cases were already passing with the existing implementation.

try {
// getMethod returns the most-specific method implementation, for public methods only (which is any method in an interface)
Method effectiveMethod = implementedType.getRawClass().getMethod(name, argTypes);
if (BeanUtil.isConcrete(effectiveMethod)) {
Copy link
Member

Choose a reason for hiding this comment

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

Quick question on this one: is the other case (not concrete) not conclusive?
That is, why not just return BeanUtil.isConcrete(effectiveMethod) as is, instead of continuing?
I am assuming here that if method is found, it is the visible declaration.

I suspect most of the time continuing would only be wasteful and is unlikely to give wrong answer....
but I have occasionally used pattern of re-abstract'ing methods (esp. toString() / equals() from Object) to force re-implementing by sub-classes.

Copy link
Contributor Author

@robbytx robbytx Sep 23, 2020

Choose a reason for hiding this comment

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

Oh, that's an interesting thought. Yes... if the method is returned, then I think you have the definitive answer as to whether the method is concrete for the implemented type.

I have occasionally used pattern of re-abstract'ing methods (esp. toString() / equals() from Object) to force re-implementing by sub-classes.

I think if the implemented type returned one of those "re-abstracted" methods, then it would be abstract, and the MrBean-generated class would be required to provide an implementation. I'm not sure how MrBean would handle this, probably it would throw an error, since it's not a property?

So, to clarify what we're talking about, I think it would be correct to simply return BeanUtil.isConcrete(effectiveMethod) here.

Copy link
Member

Choose a reason for hiding this comment

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

Ok cool.

@cowtowncoder cowtowncoder modified the milestones: 2.10.0, 2.11.3 Sep 22, 2020
cowtowncoder added a commit that referenced this pull request Sep 22, 2020
for (JavaType curr = implementedType; (curr != null) && !curr.isJavaLangObject();
curr = curr.getSuperClass()) {
// 29-Nov-2015, tatu: Avoiding exceptions would be good, so would linear scan
// be better here?
try {
Method effectiveMethod = curr.getRawClass().getDeclaredMethod(name, argTypes);
if (effectiveMethod != null && BeanUtil.isConcrete(effectiveMethod)) {
if (BeanUtil.isConcrete(effectiveMethod)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently any "re-abstracted" methods are skipped over by this check. If the concrete method exists on a class other than java.lang.Object, then I think MrBean will find it, and return true, which may be incorrect.

I'll write a test case to confirm this behavior.

Copy link
Member

Choose a reason for hiding this comment

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

Much appreciated! It's good that we can keep modules up to date with "new" versions of JDK (Java 8 isn't that new of course, but it wasn't around when Mr Bean was written and still isn't the baseline).

@robbytx robbytx deleted the patch-default-in-interfaces branch September 23, 2020 16:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants