Skip to content

Infer container element type from all elements when setting a container value - #1353

Open
poketopa wants to merge 1 commit into
naver:mainfrom
poketopa:fix/set-mixed-subtypes-into-abstract-container
Open

poketopa wants to merge 1 commit into
naver:mainfrom
poketopa:fix/set-mixed-subtypes-into-abstract-container

Conversation

@poketopa

Copy link
Copy Markdown

Summary

Fixes #1347.

Setting a container value whose elements are instances of different subtypes of the declared element type
(e.g. set("shapes", List.of(circle, square)) into List<Shape>) generated every element as the first
element's subtype. In strict mode the same call failed with
No matching results for given NodeResolvers. Invalid paths: [$.shapes[1].side].
This worked in 1.1.21 and regressed in 1.2.0.

(Optional): Description

When a container value is set, ValueAnalyzer builds two kinds of path resolvers from the value:

  • a GenericTypeResolver for the container path ($.shapes), inferred from the value, and
  • an InterfaceResolver per element path ($.shapes[0] → Circle, $.shapes[1] → Square).

GenericTypeResolverConverter.extractCollectionTypeArguments inferred the element type argument from the
first element only, so List<Shape> was rewritten to ArrayList<Circle> and every element node was
created as Circle before the per-element resolvers ran. The resolver for $.shapes[1] then saw a Circle
node, Circle.isAssignableFrom(Square) is false, and the Square value was projected onto a Circle node.
extractMapTypeArguments had the same problem with the first entry's key and value.

This change infers the element (and map key/value) type from all non-null elements:

  • all elements share one class → that class, with its own type arguments (unchanged behaviour);
  • elements have different classes → their most specific common superclass (Shape), so each element node
    keeps the declared type and the per-element InterfaceResolver picks its runtime type;
  • the only common superclass is Object (e.g. implementations of an interface, or unrelated classes) →
    no type argument is inferred and the declared element type is kept.

How Has This Been Tested?

  • ContainerElementSubtypeTest (java-tests): the two cases from Setting a list of mixed subtypes into an abstract-typed list generates every element as the first subtype (1.2.x regression) #1347 (default and strict mode), element
    order and element values preserved, mixed implementations of an interface, and mixed values in a
    Map<String, Shape>. All fail on main and pass with this change.
  • GenericTypeResolverConverterTest (object-farm-api): same class, different subclasses, unrelated classes,
    null elements, map values.
  • Regression command from CLAUDE.md:
    ./gradlew --continue clean :object-farm-api:check :fixture-monkey:test :fixture-monkey-tests:java-tests:test :fixture-monkey-tests:java-17-tests:test :fixture-monkey-tests:kotlin-tests:test (JDK 17)

Is the Document updated?

No. The behaviour matches what the documentation already describes for set with container values.

Note: #1337 also adds a GenericTypeResolverConverterTest (for a different method, createResolver).
The production changes do not overlap; if #1337 lands first I will rebase and merge the two test classes.

@CLAassistant

CLAassistant commented Oct 19, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

…er value

Setting a container value whose elements are instances of different
subtypes of the declared element type generated every element as the
first element's subtype, because the generic type argument of the
container was inferred from the first element only. Infer it from the
most specific common superclass of all elements instead, and keep the
declared element type when the only common superclass is Object.

Fixes naver#1347
@poketopa
poketopa force-pushed the fix/set-mixed-subtypes-into-abstract-container branch from d78574d to 18ea529 Compare October 19, 2026 11:36

This branch has not been deployed

No deployments
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.

Setting a list of mixed subtypes into an abstract-typed list generates every element as the first subtype (1.2.x regression)

2 participants