mirror of
https://github.com/fankes/moshi.git
synced 2025-10-19 16:09:21 +08:00
Be more aggressive about canonicalizing types.
Unfortunately we shouldn't be relying on equals() and hashCode() of the default implementations anywhere.
This commit is contained in:
@@ -228,7 +228,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
||||
List<AdapterMethod> adapterMethods, Type type, Set<? extends Annotation> annotations) {
|
||||
for (int i = 0, size = adapterMethods.size(); i < size; i++) {
|
||||
AdapterMethod adapterMethod = adapterMethods.get(i);
|
||||
if (Types.equals(adapterMethod.type, type) && adapterMethod.annotations.equals(annotations)) {
|
||||
if (adapterMethod.type.equals(type) && adapterMethod.annotations.equals(annotations)) {
|
||||
return adapterMethod;
|
||||
}
|
||||
}
|
||||
@@ -244,7 +244,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
||||
|
||||
public AdapterMethod(Type type,
|
||||
Set<? extends Annotation> annotations, Object adapter, Method method, boolean nullable) {
|
||||
this.type = type;
|
||||
this.type = Types.canonicalize(type);
|
||||
this.annotations = annotations;
|
||||
this.adapter = adapter;
|
||||
this.method = method;
|
||||
|
@@ -22,7 +22,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -63,6 +62,8 @@ public final class Moshi {
|
||||
|
||||
@SuppressWarnings("unchecked") // Factories are required to return only matching JsonAdapters.
|
||||
public <T> JsonAdapter<T> adapter(Type type, Set<? extends Annotation> annotations) {
|
||||
type = Types.canonicalize(type);
|
||||
|
||||
// If there's an equivalent adapter in the cache, we're done!
|
||||
Object cacheKey = cacheKey(type, annotations);
|
||||
synchronized (adapterCache) {
|
||||
@@ -111,6 +112,8 @@ public final class Moshi {
|
||||
@SuppressWarnings("unchecked") // Factories are required to return only matching JsonAdapters.
|
||||
public <T> JsonAdapter<T> nextAdapter(JsonAdapter.Factory skipPast, Type type,
|
||||
Set<? extends Annotation> annotations) {
|
||||
type = Types.canonicalize(type);
|
||||
|
||||
int skipPastIndex = factories.indexOf(skipPast);
|
||||
if (skipPastIndex == -1) {
|
||||
throw new IllegalArgumentException("Unable to skip past unknown factory " + skipPast);
|
||||
|
@@ -76,15 +76,18 @@ public final class Types {
|
||||
return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
|
||||
|
||||
} else if (type instanceof ParameterizedType) {
|
||||
if (type instanceof ParameterizedTypeImpl) return type;
|
||||
ParameterizedType p = (ParameterizedType) type;
|
||||
return new ParameterizedTypeImpl(p.getOwnerType(),
|
||||
p.getRawType(), p.getActualTypeArguments());
|
||||
|
||||
} else if (type instanceof GenericArrayType) {
|
||||
if (type instanceof GenericArrayTypeImpl) return type;
|
||||
GenericArrayType g = (GenericArrayType) type;
|
||||
return new GenericArrayTypeImpl(g.getGenericComponentType());
|
||||
|
||||
} else if (type instanceof WildcardType) {
|
||||
if (type instanceof WildcardTypeImpl) return type;
|
||||
WildcardType w = (WildcardType) type;
|
||||
return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
|
||||
|
||||
@@ -172,7 +175,7 @@ public final class Types {
|
||||
&& va.getName().equals(vb.getName());
|
||||
|
||||
} else {
|
||||
// This isn't a supported type. Could be a generic array type, wildcard type, etc.
|
||||
// This isn't a supported type.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -369,19 +369,7 @@ public final class AdapterMethodsTest {
|
||||
.build();
|
||||
|
||||
// This class doesn't implement equals() and hashCode() as it should.
|
||||
ParameterizedType listOfStringType = new ParameterizedType() {
|
||||
@Override public Type[] getActualTypeArguments() {
|
||||
return new Type[] { String.class };
|
||||
}
|
||||
|
||||
@Override public Type getRawType() {
|
||||
return List.class;
|
||||
}
|
||||
|
||||
@Override public Type getOwnerType() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
ParameterizedType listOfStringType = brokenParameterizedType(0, List.class, String.class);
|
||||
|
||||
JsonAdapter<List<String>> jsonAdapter = moshi.adapter(listOfStringType);
|
||||
assertThat(jsonAdapter.toJson(Arrays.asList("a", "b", "c"))).isEqualTo("\"a|b|c\"");
|
||||
@@ -403,6 +391,21 @@ public final class AdapterMethodsTest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Even when the types we use to look up JSON adapters are not equal, if they're equivalent they
|
||||
* should return the same JsonAdapter instance.
|
||||
*/
|
||||
@Test public void parameterizedTypeCacheKey() throws Exception {
|
||||
Moshi moshi = new Moshi.Builder().build();
|
||||
|
||||
Type a = brokenParameterizedType(0, List.class, String.class);
|
||||
Type b = brokenParameterizedType(1, List.class, String.class);
|
||||
Type c = brokenParameterizedType(2, List.class, String.class);
|
||||
|
||||
assertThat(moshi.adapter(b)).isSameAs(moshi.adapter(a));
|
||||
assertThat(moshi.adapter(c)).isSameAs(moshi.adapter(a));
|
||||
}
|
||||
|
||||
static class Point {
|
||||
final int x;
|
||||
final int y;
|
||||
@@ -424,4 +427,34 @@ public final class AdapterMethodsTest {
|
||||
interface Shape {
|
||||
String draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new parameterized type that doesn't implement {@link Object#equals} or {@link
|
||||
* Object#hashCode} by value. These implementation defects are consistent with the parameterized
|
||||
* type that shipped in some older versions of Android.
|
||||
*/
|
||||
ParameterizedType brokenParameterizedType(
|
||||
final int hashCode, final Class<?> rawType, final Type... typeArguments) {
|
||||
return new ParameterizedType() {
|
||||
@Override public Type[] getActualTypeArguments() {
|
||||
return typeArguments;
|
||||
}
|
||||
|
||||
@Override public Type getRawType() {
|
||||
return rawType;
|
||||
}
|
||||
|
||||
@Override public Type getOwnerType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object other) {
|
||||
return other == this;
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user