Skip to content

Commit 11c7bd6

Browse files
committed
test coverage and unlikely edges
1 parent 8c49c17 commit 11c7bd6

File tree

2 files changed

+57
-7
lines changed

2 files changed

+57
-7
lines changed

src/main/java/com/fasterxml/jackson/databind/introspect/MethodGenericTypeResolver.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,6 @@ static TypeBindings bindMethodTypeParameters(
8888
return null;
8989
}
9090

91-
// Avoid duplicates
92-
if (names.contains(typeParameterName)) {
93-
continue;
94-
}
95-
9691
JavaType bindTarget = requestedType.getBindings().getBoundType(i);
9792
if (bindTarget == null) {
9893
return null;
@@ -104,8 +99,27 @@ static TypeBindings bindMethodTypeParameters(
10499
return null;
105100
}
106101
if (pessimisticallyValidateBounds(emptyTypeResCtxt, bindTarget, methodTypeVariable.getBounds())) {
107-
names.add(methodTypeVariable.getName());
108-
types.add(bindTarget);
102+
// Avoid duplicate entries for the same type variable, e.g. '<T> Map<T, T> foo(Class<T> in)'
103+
int existingIndex = names.indexOf(typeParameterName);
104+
if (existingIndex != -1) {
105+
JavaType existingBindTarget = types.get(existingIndex);
106+
if (bindTarget.equals(existingBindTarget)) {
107+
continue;
108+
}
109+
boolean existingIsSubtype = existingBindTarget.isTypeOrSubTypeOf(bindTarget.getRawClass());
110+
boolean newIsSubtype = bindTarget.isTypeOrSubTypeOf(existingBindTarget.getRawClass());
111+
if (!existingIsSubtype && !newIsSubtype) {
112+
// No way to satisfy the requested type.
113+
return null;
114+
}
115+
if (existingIsSubtype ^ newIsSubtype && newIsSubtype) {
116+
// If the new type is more specific than the existing type, the new type replaces the old.
117+
types.set(existingIndex, bindTarget);
118+
}
119+
} else {
120+
names.add(typeParameterName);
121+
types.add(bindTarget);
122+
}
109123
}
110124
}
111125
}

src/test/java/com/fasterxml/jackson/databind/introspect/MethodGenericTypeResolverTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.fasterxml.jackson.databind.introspect;
22

3+
import com.fasterxml.jackson.annotation.JsonCreator;
34
import com.fasterxml.jackson.core.type.TypeReference;
45
import com.fasterxml.jackson.databind.JavaType;
56
import com.fasterxml.jackson.databind.type.TypeBindings;
@@ -46,6 +47,26 @@ public static <A, B> Map<A, B> multipleTypeVariables(Map<A, B> input) {
4647
throw new UnsupportedOperationException();
4748
}
4849

50+
public static class StubA {
51+
private final String value;
52+
53+
@JsonCreator
54+
private StubA(String value) {
55+
this.value = value;
56+
}
57+
58+
public String getValue() {
59+
return value;
60+
}
61+
}
62+
63+
public static class StubB extends StubA {
64+
@JsonCreator
65+
public StubB(String value) {
66+
super(value);
67+
}
68+
}
69+
4970
@Test
5071
public void testWithoutGenerics() {
5172
TypeBindings bindings = MethodGenericTypeResolver.bindMethodTypeParameters(
@@ -84,6 +105,21 @@ public void testWithRepeatedGenericInReturn() {
84105
assertThat(asMap(bindings)).isEqualTo(asMap("T", type(String.class)));
85106
}
86107

108+
@Test
109+
public void testWithRepeatedGenericInReturnWithIncreasingSpecificity() {
110+
Method method = method("mapWithSameKeysAndValues");
111+
TypeBindings bindingsAb = MethodGenericTypeResolver.bindMethodTypeParameters(
112+
method, type(new TypeReference<Map<StubA, StubB>>() {
113+
}), EMPTY_CONTEXT);
114+
TypeBindings bindingsBa = MethodGenericTypeResolver.bindMethodTypeParameters(
115+
method, type(new TypeReference<Map<StubB, StubA>>() {
116+
}), EMPTY_CONTEXT);
117+
assertThat(asMap(bindingsAb))
118+
.as("Order of parameters shouldn't impact the result")
119+
.isEqualTo(asMap(bindingsBa))
120+
.isEqualTo(asMap("T", type(StubB.class)));
121+
}
122+
87123
@Test
88124
public void testMultipleTypeVariables() {
89125
TypeBindings bindings = MethodGenericTypeResolver.bindMethodTypeParameters(

0 commit comments

Comments
 (0)