Browse Source
- Support for serialization - Allow programmatic creation of an array from a given component type - Allow programmatic creation with given generics - Extract generics from Class types using Class.getTypeParameters() - Move TypeVariableResolver to an inner class (and make method private) - Refine 'resolve()' algorithm Issue: SPR-10973pull/388/head
6 changed files with 901 additions and 200 deletions
@ -0,0 +1,330 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2013 the original author or authors. |
||||||
|
* |
||||||
|
* 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 org.springframework.core; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.ObjectInputStream; |
||||||
|
import java.io.Serializable; |
||||||
|
import java.lang.reflect.Field; |
||||||
|
import java.lang.reflect.GenericArrayType; |
||||||
|
import java.lang.reflect.InvocationHandler; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.lang.reflect.ParameterizedType; |
||||||
|
import java.lang.reflect.Proxy; |
||||||
|
import java.lang.reflect.Type; |
||||||
|
import java.lang.reflect.TypeVariable; |
||||||
|
import java.lang.reflect.WildcardType; |
||||||
|
|
||||||
|
import org.springframework.util.Assert; |
||||||
|
import org.springframework.util.ReflectionUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Internal utility class that can be used to obtain wrapped {@link Serializable} variants |
||||||
|
* of {@link java.lang.reflect.Type}s. |
||||||
|
* |
||||||
|
* <p>{@link #forField(Field) Fields} or {@link #forMethodParameter(MethodParameter) |
||||||
|
* MethodParameters} can be used as the root source for a serializable type. Alternatively |
||||||
|
* the {@link #forGenericSuperclass(Class) superclass}, |
||||||
|
* {@link #forGenericInterfaces(Class) interfaces} or {@link #forTypeParameters(Class) |
||||||
|
* type parameters} or a regular {@link Class} can also be used as source. |
||||||
|
* |
||||||
|
* <p>The returned type will either be a {@link Class} or a serializable proxy of |
||||||
|
* {@link GenericArrayType}, {@link ParameterizedType}, {@link TypeVariable} or |
||||||
|
* {@link WildcardType}. With the exception of {@link Class} (which is final) calls to |
||||||
|
* methods that return further {@link Type}s (for example |
||||||
|
* {@link GenericArrayType#getGenericComponentType()}) will be automatically wrapped. |
||||||
|
* |
||||||
|
* @author Phillip Webb |
||||||
|
* @since 4.0 |
||||||
|
*/ |
||||||
|
abstract class SerializableTypeWrapper { |
||||||
|
|
||||||
|
private static final Class<?>[] SUPPORTED_SERIALAZABLE_TYPES = { GenericArrayType.class, |
||||||
|
ParameterizedType.class, TypeVariable.class, WildcardType.class }; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Return a {@link Serializable} variant of {@link Field#getGenericType()}. |
||||||
|
*/ |
||||||
|
public static Type forField(Field field) { |
||||||
|
Assert.notNull(field, "Field must not be null"); |
||||||
|
return forTypeProvider(new FieldTypeProvider(field)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return a {@link Serializable} variant of |
||||||
|
* {@link MethodParameter#getGenericParameterType()}. |
||||||
|
*/ |
||||||
|
public static Type forMethodParameter(MethodParameter methodParameter) { |
||||||
|
return forTypeProvider(new MethodParameterTypeProvider(methodParameter)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return a {@link Serializable} variant of {@link Class#getGenericSuperclass()}. |
||||||
|
*/ |
||||||
|
public static Type forGenericSuperclass(final Class<?> type) { |
||||||
|
return forTypeProvider(new TypeProvider() { |
||||||
|
@Override |
||||||
|
public Type getType() { |
||||||
|
return type.getGenericSuperclass(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return a {@link Serializable} variant of {@link Class#getGenericInterfaces()}. |
||||||
|
*/ |
||||||
|
public static Type[] forGenericInterfaces(final Class<?> type) { |
||||||
|
Type[] result = new Type[type.getGenericInterfaces().length]; |
||||||
|
for (int i = 0; i < result.length; i++) { |
||||||
|
final int index = i; |
||||||
|
result[i] = forTypeProvider(new TypeProvider() { |
||||||
|
@Override |
||||||
|
public Type getType() { |
||||||
|
return type.getGenericInterfaces()[index]; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return a {@link Serializable} variant of {@link Class#getTypeParameters()}. |
||||||
|
*/ |
||||||
|
public static Type[] forTypeParameters(final Class<?> type) { |
||||||
|
Type[] result = new Type[type.getTypeParameters().length]; |
||||||
|
for (int i = 0; i < result.length; i++) { |
||||||
|
final int index = i; |
||||||
|
result[i] = forTypeProvider(new TypeProvider() { |
||||||
|
@Override |
||||||
|
public Type getType() { |
||||||
|
return type.getTypeParameters()[index]; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Return a {@link Serializable} {@link Type} backed by a {@link TypeProvider} . |
||||||
|
*/ |
||||||
|
private static Type forTypeProvider(final TypeProvider provider) { |
||||||
|
Assert.notNull(provider, "Provider must not be null"); |
||||||
|
if (provider.getType() instanceof Serializable || provider.getType() == null) { |
||||||
|
return provider.getType(); |
||||||
|
} |
||||||
|
for (Class<?> type : SUPPORTED_SERIALAZABLE_TYPES) { |
||||||
|
if (type.isAssignableFrom(provider.getType().getClass())) { |
||||||
|
ClassLoader classLoader = provider.getClass().getClassLoader(); |
||||||
|
Class<?>[] interfaces = new Class<?>[] { type, Serializable.class }; |
||||||
|
InvocationHandler handler = new TypeProxyInvocationHandler(provider); |
||||||
|
return (Type) Proxy.newProxyInstance(classLoader, interfaces, handler); |
||||||
|
} |
||||||
|
} |
||||||
|
throw new IllegalArgumentException("Unsupported Type class " |
||||||
|
+ provider.getType().getClass().getName()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* A {@link Serializable} interface providing access to a {@link Type}. |
||||||
|
*/ |
||||||
|
private static interface TypeProvider extends Serializable { |
||||||
|
|
||||||
|
/** |
||||||
|
* Return the (possibly non {@link Serializable}) {@link Type}. |
||||||
|
*/ |
||||||
|
Type getType(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* {@link Serializable} {@link InvocationHandler} used by the Proxied {@link Type}. |
||||||
|
* Provides serialization support and enhances any methods that return {@code Type} |
||||||
|
* or {@code Type[]}. |
||||||
|
*/ |
||||||
|
private static class TypeProxyInvocationHandler implements InvocationHandler, |
||||||
|
Serializable { |
||||||
|
|
||||||
|
private final TypeProvider provider; |
||||||
|
|
||||||
|
|
||||||
|
public TypeProxyInvocationHandler(TypeProvider provider) { |
||||||
|
this.provider = provider; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
||||||
|
if (Type.class.equals(method.getReturnType()) && args == null) { |
||||||
|
return forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, -1)); |
||||||
|
} |
||||||
|
if (Type[].class.equals(method.getReturnType()) && args == null) { |
||||||
|
Type[] result = new Type[((Type[]) method.invoke(this.provider.getType(), args)).length]; |
||||||
|
for (int i = 0; i < result.length; i++) { |
||||||
|
result[i] = forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, i)); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
return method.invoke(this.provider.getType(), args); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* {@link TypeProvider} for {@link Type}s obtained from a {@link Field}. |
||||||
|
*/ |
||||||
|
private static class FieldTypeProvider implements TypeProvider { |
||||||
|
|
||||||
|
private final String fieldName; |
||||||
|
|
||||||
|
private final Class<?> declaringClass; |
||||||
|
|
||||||
|
private transient Field field; |
||||||
|
|
||||||
|
|
||||||
|
public FieldTypeProvider(Field field) { |
||||||
|
this.fieldName = field.getName(); |
||||||
|
this.declaringClass = field.getDeclaringClass(); |
||||||
|
this.field = field; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Type getType() { |
||||||
|
return this.field.getGenericType(); |
||||||
|
} |
||||||
|
|
||||||
|
private void readObject(ObjectInputStream inputStream) throws IOException, |
||||||
|
ClassNotFoundException { |
||||||
|
inputStream.defaultReadObject(); |
||||||
|
try { |
||||||
|
this.field = this.declaringClass.getDeclaredField(this.fieldName); |
||||||
|
} |
||||||
|
catch (Throwable ex) { |
||||||
|
throw new IllegalStateException( |
||||||
|
"Could not find original class structure", ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* {@link TypeProvider} for {@link Type}s obtained from a {@link MethodParameter}. |
||||||
|
*/ |
||||||
|
private static class MethodParameterTypeProvider implements TypeProvider { |
||||||
|
|
||||||
|
private final String methodName; |
||||||
|
|
||||||
|
private final Class<?>[] parameterTypes; |
||||||
|
|
||||||
|
private final Class<?> declaringClass; |
||||||
|
|
||||||
|
private final int parameterIndex; |
||||||
|
|
||||||
|
private transient MethodParameter methodParameter; |
||||||
|
|
||||||
|
|
||||||
|
public MethodParameterTypeProvider(MethodParameter methodParameter) { |
||||||
|
if (methodParameter.getMethod() != null) { |
||||||
|
this.methodName = methodParameter.getMethod().getName(); |
||||||
|
this.parameterTypes = methodParameter.getMethod().getParameterTypes(); |
||||||
|
} |
||||||
|
else { |
||||||
|
this.methodName = null; |
||||||
|
this.parameterTypes = methodParameter.getConstructor().getParameterTypes(); |
||||||
|
} |
||||||
|
this.declaringClass = methodParameter.getDeclaringClass(); |
||||||
|
this.parameterIndex = methodParameter.getParameterIndex(); |
||||||
|
this.methodParameter = methodParameter; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Type getType() { |
||||||
|
return this.methodParameter.getGenericParameterType(); |
||||||
|
} |
||||||
|
|
||||||
|
private void readObject(ObjectInputStream inputStream) throws IOException, |
||||||
|
ClassNotFoundException { |
||||||
|
inputStream.defaultReadObject(); |
||||||
|
try { |
||||||
|
if (this.methodName != null) { |
||||||
|
this.methodParameter = new MethodParameter( |
||||||
|
this.declaringClass.getDeclaredMethod(this.methodName, |
||||||
|
this.parameterTypes), this.parameterIndex); |
||||||
|
} |
||||||
|
else { |
||||||
|
this.methodParameter = new MethodParameter( |
||||||
|
this.declaringClass.getDeclaredConstructor(this.parameterTypes), |
||||||
|
this.parameterIndex); |
||||||
|
} |
||||||
|
} |
||||||
|
catch (Throwable ex) { |
||||||
|
throw new IllegalStateException( |
||||||
|
"Could not find original class structure", ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* {@link TypeProvider} for {@link Type}s obtained by invoking a no-arg method. |
||||||
|
*/ |
||||||
|
private static class MethodInvokeTypeProvider implements TypeProvider { |
||||||
|
|
||||||
|
private final TypeProvider provider; |
||||||
|
|
||||||
|
private final String methodName; |
||||||
|
|
||||||
|
private final int index; |
||||||
|
|
||||||
|
private transient Object result; |
||||||
|
|
||||||
|
|
||||||
|
public MethodInvokeTypeProvider(TypeProvider provider, Method method, int index) { |
||||||
|
this.provider = provider; |
||||||
|
this.methodName = method.getName(); |
||||||
|
this.index = index; |
||||||
|
this.result = ReflectionUtils.invokeMethod(method, provider.getType()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Type getType() { |
||||||
|
if (this.result instanceof Type || this.result == null) { |
||||||
|
return (Type) this.result; |
||||||
|
} |
||||||
|
return ((Type[])this.result)[this.index]; |
||||||
|
} |
||||||
|
|
||||||
|
private void readObject(ObjectInputStream inputStream) throws IOException, |
||||||
|
ClassNotFoundException { |
||||||
|
inputStream.defaultReadObject(); |
||||||
|
Method method = ReflectionUtils.findMethod( |
||||||
|
this.provider.getType().getClass(), this.methodName); |
||||||
|
this.result = ReflectionUtils.invokeMethod(method, this.provider.getType()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
@ -1,40 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2002-2013 the original author or authors. |
|
||||||
* |
|
||||||
* 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 org.springframework.core; |
|
||||||
|
|
||||||
import java.lang.reflect.Type; |
|
||||||
import java.lang.reflect.TypeVariable; |
|
||||||
|
|
||||||
/** |
|
||||||
* Strategy interface that can be used to resolve {@link java.lang.reflect.TypeVariable}s. |
|
||||||
* |
|
||||||
* @author Phillip Webb |
|
||||||
* @since 4.0 |
|
||||||
* @see ResolvableType |
|
||||||
* @see GenericTypeResolver |
|
||||||
*/ |
|
||||||
interface TypeVariableResolver { |
|
||||||
|
|
||||||
/** |
|
||||||
* Resolve the specified type variable. |
|
||||||
* @param typeVariable the type variable to resolve (must not be {@code null}) |
|
||||||
* @return the resolved {@link java.lang.reflect.Type} for the variable or |
|
||||||
* {@code null} if the variable cannot be resolved. |
|
||||||
*/ |
|
||||||
Type resolveVariable(TypeVariable<?> typeVariable); |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,171 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2013 the original author or authors. |
||||||
|
* |
||||||
|
* 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 org.springframework.core; |
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream; |
||||||
|
import java.io.ByteArrayOutputStream; |
||||||
|
import java.io.ObjectInputStream; |
||||||
|
import java.io.ObjectOutputStream; |
||||||
|
import java.lang.reflect.Constructor; |
||||||
|
import java.lang.reflect.GenericArrayType; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.lang.reflect.ParameterizedType; |
||||||
|
import java.lang.reflect.Type; |
||||||
|
import java.lang.reflect.TypeVariable; |
||||||
|
import java.lang.reflect.WildcardType; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*; |
||||||
|
import static org.junit.Assert.*; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link SerializableTypeWrapper}. |
||||||
|
* |
||||||
|
* @author Phillip Webb |
||||||
|
*/ |
||||||
|
public class SerializableTypeWrapperTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void forField() throws Exception { |
||||||
|
Type type = SerializableTypeWrapper.forField(Fields.class.getField("parameterizedType")); |
||||||
|
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>")); |
||||||
|
assertSerialzable(type); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void forMethodParameter() throws Exception { |
||||||
|
Method method = Methods.class.getDeclaredMethod("method", Class.class, Object.class); |
||||||
|
Type type = SerializableTypeWrapper.forMethodParameter(MethodParameter.forMethodOrConstructor(method, 0)); |
||||||
|
assertThat(type.toString(), equalTo("java.lang.Class<T>")); |
||||||
|
assertSerialzable(type); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void forConstructor() throws Exception { |
||||||
|
Constructor<?> constructor = Constructors.class.getDeclaredConstructor(List.class); |
||||||
|
Type type = SerializableTypeWrapper.forMethodParameter(MethodParameter.forMethodOrConstructor(constructor, 0)); |
||||||
|
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>")); |
||||||
|
assertSerialzable(type); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void forGenericSuperClass() throws Exception { |
||||||
|
Type type = SerializableTypeWrapper.forGenericSuperclass(ArrayList.class); |
||||||
|
assertThat(type.toString(), equalTo("java.util.AbstractList<E>")); |
||||||
|
assertSerialzable(type); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void forGenericInterfaces() throws Exception { |
||||||
|
Type type = SerializableTypeWrapper.forGenericInterfaces(List.class)[0]; |
||||||
|
assertThat(type.toString(), equalTo("java.util.Collection<E>")); |
||||||
|
assertSerialzable(type); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void forTypeParamters() throws Exception { |
||||||
|
Type type = SerializableTypeWrapper.forTypeParameters(List.class)[0]; |
||||||
|
assertThat(type.toString(), equalTo("E")); |
||||||
|
assertSerialzable(type); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void classType() throws Exception { |
||||||
|
Type type = SerializableTypeWrapper.forField(Fields.class.getField("classType")); |
||||||
|
assertThat(type.toString(), equalTo("class java.lang.String")); |
||||||
|
assertSerialzable(type); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void genericArrayType() throws Exception { |
||||||
|
GenericArrayType type = (GenericArrayType) SerializableTypeWrapper.forField(Fields.class.getField("genericArrayType")); |
||||||
|
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>[]")); |
||||||
|
assertSerialzable(type); |
||||||
|
assertSerialzable(type.getGenericComponentType()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void parameterizedType() throws Exception { |
||||||
|
ParameterizedType type = (ParameterizedType) SerializableTypeWrapper.forField(Fields.class.getField("parameterizedType")); |
||||||
|
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>")); |
||||||
|
assertSerialzable(type); |
||||||
|
assertSerialzable(type.getOwnerType()); |
||||||
|
assertSerialzable(type.getRawType()); |
||||||
|
assertSerialzable(type.getActualTypeArguments()); |
||||||
|
assertSerialzable(type.getActualTypeArguments()[0]); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void typeVariableType() throws Exception { |
||||||
|
TypeVariable<?> type = (TypeVariable<?>) SerializableTypeWrapper.forField(Fields.class.getField("typeVariableType")); |
||||||
|
assertThat(type.toString(), equalTo("T")); |
||||||
|
assertSerialzable(type); |
||||||
|
assertSerialzable(type.getBounds()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void wildcardType() throws Exception { |
||||||
|
ParameterizedType typeSource = (ParameterizedType) SerializableTypeWrapper.forField(Fields.class.getField("wildcardType")); |
||||||
|
WildcardType type = (WildcardType) typeSource.getActualTypeArguments()[0]; |
||||||
|
assertThat(type.toString(), equalTo("? extends java.lang.CharSequence")); |
||||||
|
assertSerialzable(type); |
||||||
|
assertSerialzable(type.getLowerBounds()); |
||||||
|
assertSerialzable(type.getUpperBounds()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void assertSerialzable(Object source) throws Exception { |
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
||||||
|
ObjectOutputStream oos = new ObjectOutputStream(bos); |
||||||
|
oos.writeObject(source); |
||||||
|
oos.close(); |
||||||
|
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); |
||||||
|
assertThat(ois.readObject(), equalTo(source)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static class Fields<T> { |
||||||
|
|
||||||
|
public String classType; |
||||||
|
|
||||||
|
public List<String>[] genericArrayType; |
||||||
|
|
||||||
|
public List<String> parameterizedType; |
||||||
|
|
||||||
|
public T typeVariableType; |
||||||
|
|
||||||
|
public List<? extends CharSequence> wildcardType; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
static interface Methods { |
||||||
|
|
||||||
|
<T> List<T> method(Class<T> p1, T p2); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
static class Constructors { |
||||||
|
|
||||||
|
public Constructors(List<String> p) { |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue