Merge pull request #13 from square/scottb/types
Remove TypeLiteral, test fixes.
This commit is contained in:
commit
8fe0b22fba
5 changed files with 43 additions and 129 deletions
|
@ -1,105 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2008 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.squareup.moshi;
|
|
||||||
|
|
||||||
import java.lang.reflect.ParameterizedType;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a generic type {@code T}. Java doesn't yet provide a way to represent generic types,
|
|
||||||
* so this class does. Forces clients to create a subclass of this class which enables retrieval the
|
|
||||||
* type information even at runtime.
|
|
||||||
*
|
|
||||||
* <p>For example, to create a type literal for {@code List<String>}, you can create an empty
|
|
||||||
* anonymous inner class: <pre> {@code
|
|
||||||
*
|
|
||||||
* TypeToken<List<String>> list = new TypeToken<List<String>>() {};
|
|
||||||
* }</pre>
|
|
||||||
*
|
|
||||||
* <p>This syntax cannot be used to create type literals that have wildcard parameters, such as
|
|
||||||
* {@code Class<?>} or {@code List<? extends CharSequence>}.
|
|
||||||
*
|
|
||||||
* @author Bob Lee
|
|
||||||
* @author Sven Mawson
|
|
||||||
* @author Jesse Wilson
|
|
||||||
*/
|
|
||||||
public class TypeLiteral<T> {
|
|
||||||
final Class<? super T> rawType;
|
|
||||||
final Type type;
|
|
||||||
final int hashCode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new type literal. Derives represented class from type parameter.
|
|
||||||
*
|
|
||||||
* <p>Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
|
|
||||||
* anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected TypeLiteral() {
|
|
||||||
this.type = getSuperclassTypeParameter(getClass());
|
|
||||||
this.rawType = (Class<? super T>) Types.getRawType(type);
|
|
||||||
this.hashCode = type.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Unsafe. Constructs a type literal manually. */
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
TypeLiteral(Type type) {
|
|
||||||
if (type == null) throw new NullPointerException("type == null");
|
|
||||||
this.type = Types.canonicalize(type);
|
|
||||||
this.rawType = (Class<? super T>) Types.getRawType(this.type);
|
|
||||||
this.hashCode = this.type.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the type from super class's type parameter in canonical for}. */
|
|
||||||
static Type getSuperclassTypeParameter(Class<?> subclass) {
|
|
||||||
Type superclass = subclass.getGenericSuperclass();
|
|
||||||
if (superclass instanceof Class) throw new IllegalArgumentException("Missing type parameter.");
|
|
||||||
ParameterizedType parameterized = (ParameterizedType) superclass;
|
|
||||||
return Types.canonicalize(parameterized.getActualTypeArguments()[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the raw (non-generic) type for this type literal. */
|
|
||||||
public final Class<? super T> getRawType() {
|
|
||||||
return rawType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Type getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public final int hashCode() {
|
|
||||||
return this.hashCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public final boolean equals(Object o) {
|
|
||||||
return o instanceof TypeLiteral<?>
|
|
||||||
&& Types.equals(type, ((TypeLiteral<?>) o).type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public final String toString() {
|
|
||||||
return Types.typeToString(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns a type literal for {@code type}. */
|
|
||||||
public static TypeLiteral<?> get(Type type) {
|
|
||||||
return new TypeLiteral<Object>(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns a type literal for {@code type}. */
|
|
||||||
public static <T> TypeLiteral<T> get(Class<T> type) {
|
|
||||||
return new TypeLiteral<T>(type);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -41,12 +41,22 @@ final class Types {
|
||||||
private Types() {
|
private Types() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new parameterized type, applying {@code typeArguments} to {@code rawType}.
|
||||||
|
*/
|
||||||
|
public static ParameterizedType newParameterizedType(Type rawType, Type... typeArguments) {
|
||||||
|
return new ParameterizedTypeImpl(null, rawType, typeArguments);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new parameterized type, applying {@code typeArguments} to {@code rawType} and
|
* Returns a new parameterized type, applying {@code typeArguments} to {@code rawType} and
|
||||||
* enclosed by {@code ownerType}.
|
* enclosed by {@code ownerType}.
|
||||||
*/
|
*/
|
||||||
public static ParameterizedType newParameterizedTypeWithOwner(
|
public static ParameterizedType newParameterizedTypeWithOwner(
|
||||||
Type ownerType, Type rawType, Type... typeArguments) {
|
Type ownerType, Type rawType, Type... typeArguments) {
|
||||||
|
if (ownerType == null) {
|
||||||
|
throw new NullPointerException("ownerType");
|
||||||
|
}
|
||||||
return new ParameterizedTypeImpl(ownerType, rawType, typeArguments);
|
return new ParameterizedTypeImpl(ownerType, rawType, typeArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +344,7 @@ final class Types {
|
||||||
}
|
}
|
||||||
|
|
||||||
return changed
|
return changed
|
||||||
? newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args)
|
? new ParameterizedTypeImpl(newOwnerType, original.getRawType(), args)
|
||||||
: original;
|
: original;
|
||||||
|
|
||||||
} else if (toResolve instanceof WildcardType) {
|
} else if (toResolve instanceof WildcardType) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ import static com.squareup.moshi.JsonToken.NAME;
|
||||||
import static com.squareup.moshi.JsonToken.NULL;
|
import static com.squareup.moshi.JsonToken.NULL;
|
||||||
import static com.squareup.moshi.JsonToken.NUMBER;
|
import static com.squareup.moshi.JsonToken.NUMBER;
|
||||||
import static com.squareup.moshi.JsonToken.STRING;
|
import static com.squareup.moshi.JsonToken.STRING;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
@ -337,7 +338,8 @@ public final class JsonReaderTest {
|
||||||
try {
|
try {
|
||||||
reader.nextDouble();
|
reader.nextDouble();
|
||||||
fail();
|
fail();
|
||||||
} catch (IOException expected) {
|
} catch (NumberFormatException expected) {
|
||||||
|
assertThat(expected).hasMessageContaining("NaN");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -568,8 +568,8 @@ public final class MoshiTest {
|
||||||
|
|
||||||
@Test public void listJsonAdapter() throws Exception {
|
@Test public void listJsonAdapter() throws Exception {
|
||||||
Moshi moshi = new Moshi.Builder().build();
|
Moshi moshi = new Moshi.Builder().build();
|
||||||
JsonAdapter<List<String>> adapter = moshi.adapter(new TypeLiteral<List<String>>() {
|
JsonAdapter<List<String>> adapter =
|
||||||
}.getType());
|
moshi.adapter(Types.newParameterizedType(List.class, String.class));
|
||||||
assertThat(adapter.toJson(Arrays.asList("a", "b"))).isEqualTo("[\"a\",\"b\"]");
|
assertThat(adapter.toJson(Arrays.asList("a", "b"))).isEqualTo("[\"a\",\"b\"]");
|
||||||
assertThat(adapter.fromJson("[\"a\",\"b\"]")).isEqualTo(Arrays.asList("a", "b"));
|
assertThat(adapter.fromJson("[\"a\",\"b\"]")).isEqualTo(Arrays.asList("a", "b"));
|
||||||
}
|
}
|
||||||
|
@ -580,7 +580,8 @@ public final class MoshiTest {
|
||||||
set.add("b");
|
set.add("b");
|
||||||
|
|
||||||
Moshi moshi = new Moshi.Builder().build();
|
Moshi moshi = new Moshi.Builder().build();
|
||||||
JsonAdapter<Set<String>> adapter = moshi.adapter(new TypeLiteral<Set<String>>() {}.getType());
|
JsonAdapter<Set<String>> adapter =
|
||||||
|
moshi.adapter(Types.newParameterizedType(Set.class, String.class));
|
||||||
assertThat(adapter.toJson(set)).isEqualTo("[\"a\",\"b\"]");
|
assertThat(adapter.toJson(set)).isEqualTo("[\"a\",\"b\"]");
|
||||||
assertThat(adapter.fromJson("[\"a\",\"b\"]")).isEqualTo(set);
|
assertThat(adapter.fromJson("[\"a\",\"b\"]")).isEqualTo(set);
|
||||||
}
|
}
|
||||||
|
@ -591,8 +592,8 @@ public final class MoshiTest {
|
||||||
collection.add("b");
|
collection.add("b");
|
||||||
|
|
||||||
Moshi moshi = new Moshi.Builder().build();
|
Moshi moshi = new Moshi.Builder().build();
|
||||||
JsonAdapter<Collection<String>> adapter = moshi.adapter(
|
JsonAdapter<Collection<String>> adapter =
|
||||||
new TypeLiteral<Collection<String>>() {}.getType());
|
moshi.adapter(Types.newParameterizedType(Collection.class, String.class));
|
||||||
assertThat(adapter.toJson(collection)).isEqualTo("[\"a\",\"b\"]");
|
assertThat(adapter.toJson(collection)).isEqualTo("[\"a\",\"b\"]");
|
||||||
assertThat(adapter.fromJson("[\"a\",\"b\"]")).containsExactly("a", "b");
|
assertThat(adapter.fromJson("[\"a\",\"b\"]")).containsExactly("a", "b");
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,36 +28,53 @@ import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
public class TypesTest {
|
public class TypesTest {
|
||||||
@Test public void newParameterizedTypeWithoutOwner() throws Exception {
|
|
||||||
|
@Test public void newParameterizedType() throws Exception {
|
||||||
// List<A>. List is a top-level class.
|
// List<A>. List is a top-level class.
|
||||||
Type type = Types.newParameterizedTypeWithOwner(null, List.class, A.class);
|
Type type = Types.newParameterizedType(List.class, A.class);
|
||||||
assertThat(getFirstTypeArgument(type)).isEqualTo(A.class);
|
assertThat(getFirstTypeArgument(type)).isEqualTo(A.class);
|
||||||
|
|
||||||
// A<B>. A is a static inner class.
|
// A<B>. A is a static inner class.
|
||||||
type = Types.newParameterizedTypeWithOwner(null, A.class, B.class);
|
type = Types.newParameterizedType(A.class, B.class);
|
||||||
assertThat(getFirstTypeArgument(type)).isEqualTo(B.class);
|
assertThat(getFirstTypeArgument(type)).isEqualTo(B.class);
|
||||||
|
|
||||||
final class D {
|
final class D {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// D<A> is not allowed since D is not a static inner class.
|
// D<A> is not allowed since D is not a static inner class.
|
||||||
Types.newParameterizedTypeWithOwner(null, D.class, A.class);
|
Types.newParameterizedType(D.class, A.class);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A<D> is allowed.
|
// A<D> is allowed.
|
||||||
type = Types.newParameterizedTypeWithOwner(null, A.class, D.class);
|
type = Types.newParameterizedType(A.class, D.class);
|
||||||
assertThat(getFirstTypeArgument(type)).isEqualTo(D.class);
|
assertThat(getFirstTypeArgument(type)).isEqualTo(D.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void getFirstTypeArgument() throws Exception {
|
@Test public void getFirstTypeArgument() throws Exception {
|
||||||
assertNull(getFirstTypeArgument(A.class));
|
assertNull(getFirstTypeArgument(A.class));
|
||||||
|
|
||||||
Type type = Types.newParameterizedTypeWithOwner(null, A.class, B.class, C.class);
|
Type type = Types.newParameterizedType(A.class, B.class, C.class);
|
||||||
assertThat(getFirstTypeArgument(type)).isEqualTo(B.class);
|
assertThat(getFirstTypeArgument(type)).isEqualTo(B.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void newParameterizedTypeObjectMethods() throws Exception {
|
||||||
|
Type mapOfStringIntegerType = TypesTest.class.getDeclaredField(
|
||||||
|
"mapOfStringInteger").getGenericType();
|
||||||
|
ParameterizedType newMapType = Types.newParameterizedType(Map.class, String.class, Integer.class);
|
||||||
|
assertThat(newMapType).isEqualTo(mapOfStringIntegerType);
|
||||||
|
assertThat(newMapType.hashCode()).isEqualTo(mapOfStringIntegerType.hashCode());
|
||||||
|
assertThat(newMapType.toString()).isEqualTo(mapOfStringIntegerType.toString());
|
||||||
|
|
||||||
|
Type arrayListOfMapOfStringIntegerType = TypesTest.class.getDeclaredField(
|
||||||
|
"arrayListOfMapOfStringInteger").getGenericType();
|
||||||
|
ParameterizedType newListType = Types.newParameterizedType(ArrayList.class, newMapType);
|
||||||
|
assertThat(newListType).isEqualTo(arrayListOfMapOfStringIntegerType);
|
||||||
|
assertThat(newListType.hashCode()).isEqualTo(arrayListOfMapOfStringIntegerType.hashCode());
|
||||||
|
assertThat(newListType.toString()).isEqualTo(arrayListOfMapOfStringIntegerType.toString());
|
||||||
|
}
|
||||||
|
|
||||||
private static final class A {
|
private static final class A {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,17 +102,6 @@ public class TypesTest {
|
||||||
interface StringIntegerMap extends Map<String, Integer> {
|
interface StringIntegerMap extends Map<String, Integer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void typeLiteral() throws Exception {
|
|
||||||
TypeLiteral<Map<String, Integer>> typeLiteral = new TypeLiteral<Map<String, Integer>>() {};
|
|
||||||
TypeLiteral<?> fromReflection = TypeLiteral.get(
|
|
||||||
TypesTest.class.getDeclaredField("mapOfStringInteger").getGenericType());
|
|
||||||
assertThat(typeLiteral).isEqualTo(fromReflection);
|
|
||||||
assertThat(typeLiteral.hashCode()).isEqualTo(fromReflection.hashCode());
|
|
||||||
assertThat(typeLiteral.toString())
|
|
||||||
.isEqualTo("java.util.Map<java.lang.String, java.lang.Integer>");
|
|
||||||
assertThat(typeLiteral.getRawType()).isEqualTo(Map.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void arrayComponentType() throws Exception {
|
@Test public void arrayComponentType() throws Exception {
|
||||||
assertThat(Types.arrayComponentType(String[][].class)).isEqualTo(String[].class);
|
assertThat(Types.arrayComponentType(String[][].class)).isEqualTo(String[].class);
|
||||||
assertThat(Types.arrayComponentType(String[].class)).isEqualTo(String.class);
|
assertThat(Types.arrayComponentType(String[].class)).isEqualTo(String.class);
|
||||||
|
|
Loading…
Reference in a new issue