Skip to content

Commit 4c42dd5

Browse files
authored
Merge pull request #3512 from guanchengang/optimize
Improvements on TypeParameterResolver: - Fixed unexpected `equals()` results - Better `toString()` output when the `ParameterizedType` is an inner class
2 parents 9a091a3 + 06f830e commit 4c42dd5

File tree

3 files changed

+153
-18
lines changed

3 files changed

+153
-18
lines changed

src/main/java/org/apache/ibatis/reflection/TypeParameterResolver.java

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,10 @@ private static Type resolveGenericArrayType(GenericArrayType genericArrayType, T
129129
private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType,
130130
Class<?> declaringClass) {
131131
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
132+
Type ownerType = parameterizedType.getOwnerType();
132133
Type[] typeArgs = parameterizedType.getActualTypeArguments();
133134
Type[] args = resolveTypes(typeArgs, srcType, declaringClass);
134-
return new ParameterizedTypeImpl(rawType, null, args);
135+
return new ParameterizedTypeImpl(rawType, ownerType, args);
135136
}
136137

137138
private static Type resolveWildcardType(WildcardType wildcardType, Type srcType, Class<?> declaringClass) {
@@ -230,7 +231,8 @@ private static ParameterizedType translateParentTypeVars(ParameterizedType srcTy
230231
newParentArgs[i] = parentTypeArgs[i];
231232
}
232233
}
233-
return noChange ? parentType : new ParameterizedTypeImpl((Class<?>) parentType.getRawType(), null, newParentArgs);
234+
return noChange ? parentType
235+
: new ParameterizedTypeImpl((Class<?>) parentType.getRawType(), parentType.getOwnerType(), newParentArgs);
234236
}
235237

236238
private TypeParameterResolver() {
@@ -244,7 +246,7 @@ static class ParameterizedTypeImpl implements ParameterizedType {
244246

245247
private final Type[] actualTypeArguments;
246248

247-
public ParameterizedTypeImpl(Class<?> rawType, Type ownerType, Type[] actualTypeArguments) {
249+
ParameterizedTypeImpl(Class<?> rawType, Type ownerType, Type[] actualTypeArguments) {
248250
super();
249251
this.rawType = rawType;
250252
this.ownerType = ownerType;
@@ -283,14 +285,25 @@ public boolean equals(Object obj) {
283285

284286
@Override
285287
public String toString() {
286-
StringBuilder s = new StringBuilder().append(rawType.getName()).append("<");
287-
for (int i = 0; i < actualTypeArguments.length; i++) {
288-
if (i > 0) {
289-
s.append(", ");
288+
StringBuilder s = new StringBuilder();
289+
if (ownerType != null) {
290+
s.append(ownerType.getTypeName()).append("$");
291+
s.append(rawType.getSimpleName());
292+
} else {
293+
s.append(rawType.getName());
294+
}
295+
int argLength = actualTypeArguments.length;
296+
if (argLength > 0) {
297+
s.append("<");
298+
for (int i = 0; i < argLength; i++) {
299+
if (i > 0) {
300+
s.append(", ");
301+
}
302+
s.append(actualTypeArguments[i].getTypeName());
290303
}
291-
s.append(actualTypeArguments[i].getTypeName());
304+
s.append(">");
292305
}
293-
return s.append(">").toString();
306+
return s.toString();
294307
}
295308
}
296309

@@ -329,11 +342,11 @@ public boolean equals(Object obj) {
329342
if (this == obj) {
330343
return true;
331344
}
332-
if (!(obj instanceof WildcardTypeImpl)) {
345+
if (!(obj instanceof WildcardType)) {
333346
return false;
334347
}
335-
WildcardTypeImpl other = (WildcardTypeImpl) obj;
336-
return Arrays.equals(lowerBounds, other.lowerBounds) && Arrays.equals(upperBounds, other.upperBounds);
348+
WildcardType other = (WildcardType) obj;
349+
return Arrays.equals(lowerBounds, other.getLowerBounds()) && Arrays.equals(upperBounds, other.getUpperBounds());
337350
}
338351

339352
@Override
@@ -371,16 +384,16 @@ public boolean equals(Object obj) {
371384
if (this == obj) {
372385
return true;
373386
}
374-
if (!(obj instanceof GenericArrayTypeImpl)) {
387+
if (!(obj instanceof GenericArrayType)) {
375388
return false;
376389
}
377-
GenericArrayTypeImpl other = (GenericArrayTypeImpl) obj;
378-
return Objects.equals(genericComponentType, other.genericComponentType);
390+
GenericArrayType other = (GenericArrayType) obj;
391+
return Objects.equals(genericComponentType, other.getGenericComponentType());
379392
}
380393

381394
@Override
382395
public String toString() {
383-
return new StringBuilder().append(genericComponentType.toString()).append("[]").toString();
396+
return new StringBuilder().append(genericComponentType.getTypeName()).append("[]").toString();
384397
}
385398
}
386399
}

src/test/java/org/apache/ibatis/reflection/TypeParameterResolverTest.java

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.junit.jupiter.api.Assertions.assertEquals;
1919
import static org.junit.jupiter.api.Assertions.assertTrue;
2020

21+
import java.io.Serializable;
2122
import java.lang.reflect.Field;
2223
import java.lang.reflect.GenericArrayType;
2324
import java.lang.reflect.Method;
@@ -27,6 +28,7 @@
2728
import java.util.Date;
2829
import java.util.List;
2930
import java.util.Map;
31+
import java.util.Map.Entry;
3032
import java.util.concurrent.ExecutorService;
3133
import java.util.concurrent.Executors;
3234
import java.util.concurrent.Future;
@@ -230,7 +232,6 @@ void returnLv2ArrayOfArray() throws Exception {
230232
Type result = TypeParameterResolver.resolveReturnType(method, clazz);
231233
assertTrue(result instanceof Class);
232234
Class<?> resultClass = (Class<?>) result;
233-
assertTrue(result instanceof Class);
234235
assertTrue(resultClass.isArray());
235236
assertTrue(resultClass.getComponentType().isArray());
236237
assertEquals(String.class, resultClass.getComponentType().getComponentType());
@@ -553,4 +554,120 @@ public List<Integer> m() {
553554
}
554555
}
555556

557+
@Test
558+
void shouldParameterizedTypesWithOwnerTypeBeEqual() throws Exception {
559+
class Clazz {
560+
@SuppressWarnings("unused")
561+
public Entry<String, Integer> entry() {
562+
return null;
563+
}
564+
}
565+
566+
Type typeJdk = Clazz.class.getMethod("entry").getGenericReturnType();
567+
568+
Class<?> clazz = Level2Mapper.class;
569+
Method method = clazz.getMethod("selectEntry");
570+
Type typeMybatis = TypeParameterResolver.resolveReturnType(method, clazz);
571+
572+
assertTrue(
573+
typeJdk instanceof ParameterizedType && !(typeJdk instanceof TypeParameterResolver.ParameterizedTypeImpl));
574+
assertTrue(typeMybatis instanceof TypeParameterResolver.ParameterizedTypeImpl);
575+
assertTrue(typeJdk.equals(typeMybatis));
576+
assertTrue(typeMybatis.equals(typeJdk));
577+
}
578+
579+
@Test
580+
void shouldWildcardTypeBeEqual() throws Exception {
581+
class WildcardTypeTester {
582+
@SuppressWarnings("unused")
583+
public List<? extends Serializable> foo() {
584+
return null;
585+
}
586+
}
587+
588+
Class<?> clazz = WildcardTypeTester.class;
589+
Method foo = clazz.getMethod("foo");
590+
Type typeMybatis = TypeParameterResolver.resolveReturnType(foo, clazz);
591+
Type typeJdk = foo.getGenericReturnType();
592+
593+
Type wildcardMybatis = ((ParameterizedType) typeMybatis).getActualTypeArguments()[0];
594+
Type wildcardJdk = ((ParameterizedType) typeJdk).getActualTypeArguments()[0];
595+
596+
assertTrue(wildcardJdk instanceof WildcardType && !(wildcardJdk instanceof TypeParameterResolver.WildcardTypeImpl));
597+
assertTrue(wildcardMybatis instanceof TypeParameterResolver.WildcardTypeImpl);
598+
assertTrue(typeJdk.equals(typeMybatis));
599+
assertTrue(typeMybatis.equals(typeJdk));
600+
}
601+
602+
@Test
603+
void shouldGenericArrayTypeBeEqual() throws Exception {
604+
class GenericArrayTypeTester {
605+
@SuppressWarnings("unused")
606+
public List<String>[] foo() {
607+
return null;
608+
}
609+
}
610+
611+
Class<?> clazz = GenericArrayTypeTester.class;
612+
Method foo = clazz.getMethod("foo");
613+
Type typeMybatis = TypeParameterResolver.resolveReturnType(foo, clazz);
614+
Type typeJdk = foo.getGenericReturnType();
615+
616+
assertTrue(typeJdk instanceof GenericArrayType && !(typeJdk instanceof TypeParameterResolver.GenericArrayTypeImpl));
617+
assertTrue(typeMybatis instanceof TypeParameterResolver.GenericArrayTypeImpl);
618+
assertTrue(typeJdk.equals(typeMybatis));
619+
assertTrue(typeMybatis.equals(typeJdk));
620+
}
621+
622+
@Test
623+
void shouldNestedParamTypeToStringOmitCommonFqn() throws Exception {
624+
Class<?> clazz = Level2Mapper.class;
625+
Method method = clazz.getMethod("selectMapEntry");
626+
Type type = TypeParameterResolver.resolveReturnType(method, clazz);
627+
assertEquals("java.util.Map<java.util.Map$Entry<java.lang.String, java.lang.Integer>, java.util.Date>",
628+
type.toString());
629+
}
630+
631+
static class Outer<T> {
632+
633+
class Inner {
634+
}
635+
636+
public Inner foo() {
637+
return null;
638+
}
639+
640+
}
641+
642+
static class InnerTester {
643+
644+
public Outer<?>.Inner noTypeOuter() {
645+
return null;
646+
}
647+
648+
public Outer<String>.Inner stringTypeOuter() {
649+
return null;
650+
}
651+
652+
}
653+
654+
@Test
655+
void shouldToStringHandleInnerClass() throws Exception {
656+
Class<?> outerClass = Outer.class;
657+
Class<?> innerTesterClass = InnerTester.class;
658+
Method foo = outerClass.getMethod("foo");
659+
Method noTypeOuter = innerTesterClass.getMethod("noTypeOuter");
660+
Method stringTypeOuter = innerTesterClass.getMethod("stringTypeOuter");
661+
662+
Type fooReturnType = TypeParameterResolver.resolveReturnType(foo, outerClass);
663+
Type noTypeOuterReturnType = TypeParameterResolver.resolveReturnType(noTypeOuter, innerTesterClass);
664+
Type stringTypeOuterReturnType = TypeParameterResolver.resolveReturnType(stringTypeOuter, innerTesterClass);
665+
666+
assertEquals("org.apache.ibatis.reflection.TypeParameterResolverTest$Outer<T>$Inner", fooReturnType.toString());
667+
assertEquals("org.apache.ibatis.reflection.TypeParameterResolverTest$Outer<?>$Inner",
668+
noTypeOuterReturnType.toString());
669+
assertEquals("org.apache.ibatis.reflection.TypeParameterResolverTest$Outer<java.lang.String>$Inner",
670+
stringTypeOuterReturnType.toString());
671+
}
672+
556673
}

src/test/java/org/apache/ibatis/reflection/typeparam/Level0Mapper.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2022 the original author or authors.
2+
* Copyright 2009-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
1717

1818
import java.util.List;
1919
import java.util.Map;
20+
import java.util.Map.Entry;
2021

2122
public interface Level0Mapper<L, M, N> {
2223

@@ -46,6 +47,10 @@ public interface Level0Mapper<L, M, N> {
4647

4748
Map<N, M> selectMap();
4849

50+
Entry<N, M> selectEntry();
51+
52+
Map<Entry<N, M>, L> selectMapEntry();
53+
4954
N[] selectArray(List<N>[] param);
5055

5156
N[][] selectArrayOfArray();

0 commit comments

Comments
 (0)