Resolve forwarded type arguments across POJO hierarchy edges#2014
Open
vbabanin wants to merge 10 commits into
Open
Resolve forwarded type arguments across POJO hierarchy edges#2014vbabanin wants to merge 10 commits into
vbabanin wants to merge 10 commits into
Conversation
Adds substitution-aware TypeData.newInstance and wires PojoBuilderHelper to use it at both superclass and interface edges. Closes JAVA-5110 and JAVA-6138.
Member
|
@vbabanin I shouldn't have pushed my changes here (reverted) I have created a PR for additions to this branch: vbabanin#2 Initial thoughts are this looks good and I prefer over #2015 as its much more robust. |
Holder/NonGeneric fields must be private (VisibilityModifier); they are read via reflection so accessors are unnecessary. Also drop the unused Holder.scalar field. JAVA-6065
resolveTypeArgument previously fell through to Object for a WildcardType, so a wildcard nested inside a forwarded supertype argument (e.g. extends Base<List<? extends Number>>) lost its bound. Resolve it to its upper bound, mirroring getNestedTypeData, so a type variable inside the bound is still substituted against the current context. Adds a TypeData unit test for the bound resolution and a discriminated POJO round-trip (ForwardingWildcardModel) that fails without the fix. JAVA-6065
The two newInstance overloads duplicated near-identical type-argument walking. resolveTypeArgument is a strict superset of getNestedTypeData (with an empty context, type variables erase to Object exactly as before), and it also fixes a latent ClassCastException when a wildcard's upper bound is itself parameterized (e.g. ? extends List<String>). Delegate the two-arg newInstance to it and remove getNestedTypeData. JAVA-6065
Member
Author
|
Thank you, @rozza! I cherry-picked your suggested changes onto this branch. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
After JAVA-3815, encoding a POJO that uses the following hierarchy shape fails:
Encoding a
ConcreteModelinstance throws:The
valueproperty - which should resolve toMap<String, Object>- is being resolved toObjectinstead.Root cause
The underlying problem -
cachePropertyTypeDataunconditionally overwriting a property's resolution on later visits - already existed with superclass chains (JAVA-5110). Same root cause, different symptom: there, a forwarding chain likeC extends B<String>,B<T> extends A<T>,A<K>resolvesKtoObjectbecause theTypeVariableis never substituted. Users with that shape have been hitting it since at least 2023.JAVA-3815 added interface scanning, which exposed the same overwrite problem for a new set of users: because
AbstractModel<V>implementsInterfaceModel<V>, the same property is now visited twice - first fromAbstractModelwith the correct concrete TypeData, then fromInterfaceModelwith stale TypeData. The second visit silently overwrote the first, surfacing the regression for users who had never changed their code.Walk trace for
ConcreteModel:parentClassTypeDatapassedConcreteModelnull(root)AbstractModelTypeData(ConcreteModel, [Map<String,Object>])- correctInterfaceModel(via AbstractModel's interfaces)TypeData(AbstractModel)- no type paramsWhen the walker scans
InterfaceModel, it findsgetValue()again and callscachePropertyTypeData("value", ..., TypeData(AbstractModel)). This overwrites the correct resolution (Map<String,Object>) withTypeData(Object).Fix
TypeData.newInstance- new composing overload that accepts the current class's type parameters and resolved TypeData as context. When a type argument in an extends/implements clause is aTypeVariable, it is substituted against that context rather than erased toObject. NestedParameterizedTypearguments (e.g.List<T>) are handled recursively.PojoBuilderHelper.getClassHierarchy- both the superclass edge and per-interface edge now use the composingTypeData.newInstance, so TypeVariables in extends/implements clauses are substituted against the current class's resolved type arguments rather than erased to Object.JAVA-6065
JAVA-5110
JAVA-6138