]>
Commit | Line | Data |
---|---|---|
cfd903b6 MG |
1 | /** |
2 | * Copyright (C) 2008 Google Inc. | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | package com.google.gson.internal; | |
18 | ||
19 | import static com.google.gson.internal.$Gson$Preconditions.checkArgument; | |
20 | import static com.google.gson.internal.$Gson$Preconditions.checkNotNull; | |
21 | ||
22 | import java.io.Serializable; | |
23 | import java.lang.reflect.Array; | |
24 | import java.lang.reflect.GenericArrayType; | |
25 | import java.lang.reflect.GenericDeclaration; | |
26 | import java.lang.reflect.ParameterizedType; | |
27 | import java.lang.reflect.Type; | |
28 | import java.lang.reflect.TypeVariable; | |
29 | import java.lang.reflect.WildcardType; | |
30 | import java.util.Arrays; | |
31 | import java.util.Collection; | |
32 | import java.util.Map; | |
33 | import java.util.NoSuchElementException; | |
34 | import java.util.Properties; | |
35 | ||
36 | /** | |
37 | * Static methods for working with types. | |
38 | * | |
39 | * @author Bob Lee | |
40 | * @author Jesse Wilson | |
41 | */ | |
42 | public final class $Gson$Types { | |
43 | static final Type[] EMPTY_TYPE_ARRAY = new Type[] {}; | |
44 | ||
45 | private $Gson$Types() {} | |
46 | ||
47 | /** | |
48 | * Returns a new parameterized type, applying {@code typeArguments} to | |
49 | * {@code rawType} and enclosed by {@code ownerType}. | |
50 | * | |
51 | * @return a {@link java.io.Serializable serializable} parameterized type. | |
52 | */ | |
53 | public static ParameterizedType newParameterizedTypeWithOwner( | |
54 | Type ownerType, Type rawType, Type... typeArguments) { | |
55 | return new ParameterizedTypeImpl(ownerType, rawType, typeArguments); | |
56 | } | |
57 | ||
58 | /** | |
59 | * Returns an array type whose elements are all instances of | |
60 | * {@code componentType}. | |
61 | * | |
62 | * @return a {@link java.io.Serializable serializable} generic array type. | |
63 | */ | |
64 | public static GenericArrayType arrayOf(Type componentType) { | |
65 | return new GenericArrayTypeImpl(componentType); | |
66 | } | |
67 | ||
68 | /** | |
69 | * Returns a type that represents an unknown type that extends {@code bound}. | |
70 | * For example, if {@code bound} is {@code CharSequence.class}, this returns | |
71 | * {@code ? extends CharSequence}. If {@code bound} is {@code Object.class}, | |
72 | * this returns {@code ?}, which is shorthand for {@code ? extends Object}. | |
73 | */ | |
74 | public static WildcardType subtypeOf(Type bound) { | |
75 | return new WildcardTypeImpl(new Type[] { bound }, EMPTY_TYPE_ARRAY); | |
76 | } | |
77 | ||
78 | /** | |
79 | * Returns a type that represents an unknown supertype of {@code bound}. For | |
80 | * example, if {@code bound} is {@code String.class}, this returns {@code ? | |
81 | * super String}. | |
82 | */ | |
83 | public static WildcardType supertypeOf(Type bound) { | |
84 | return new WildcardTypeImpl(new Type[] { Object.class }, new Type[] { bound }); | |
85 | } | |
86 | ||
87 | /** | |
88 | * Returns a type that is functionally equal but not necessarily equal | |
89 | * according to {@link Object#equals(Object) Object.equals()}. The returned | |
90 | * type is {@link java.io.Serializable}. | |
91 | */ | |
92 | public static Type canonicalize(Type type) { | |
93 | if (type instanceof Class) { | |
94 | Class<?> c = (Class<?>) type; | |
95 | return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c; | |
96 | ||
97 | } else if (type instanceof ParameterizedType) { | |
98 | ParameterizedType p = (ParameterizedType) type; | |
99 | return new ParameterizedTypeImpl(p.getOwnerType(), | |
100 | p.getRawType(), p.getActualTypeArguments()); | |
101 | ||
102 | } else if (type instanceof GenericArrayType) { | |
103 | GenericArrayType g = (GenericArrayType) type; | |
104 | return new GenericArrayTypeImpl(g.getGenericComponentType()); | |
105 | ||
106 | } else if (type instanceof WildcardType) { | |
107 | WildcardType w = (WildcardType) type; | |
108 | return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds()); | |
109 | ||
110 | } else { | |
111 | // type is either serializable as-is or unsupported | |
112 | return type; | |
113 | } | |
114 | } | |
115 | ||
116 | public static Class<?> getRawType(Type type) { | |
117 | if (type instanceof Class<?>) { | |
118 | // type is a normal class. | |
119 | return (Class<?>) type; | |
120 | ||
121 | } else if (type instanceof ParameterizedType) { | |
122 | ParameterizedType parameterizedType = (ParameterizedType) type; | |
123 | ||
124 | // I'm not exactly sure why getRawType() returns Type instead of Class. | |
125 | // Neal isn't either but suspects some pathological case related | |
126 | // to nested classes exists. | |
127 | Type rawType = parameterizedType.getRawType(); | |
128 | checkArgument(rawType instanceof Class); | |
129 | return (Class<?>) rawType; | |
130 | ||
131 | } else if (type instanceof GenericArrayType) { | |
132 | Type componentType = ((GenericArrayType)type).getGenericComponentType(); | |
133 | return Array.newInstance(getRawType(componentType), 0).getClass(); | |
134 | ||
135 | } else if (type instanceof TypeVariable) { | |
136 | // we could use the variable's bounds, but that won't work if there are multiple. | |
137 | // having a raw type that's more general than necessary is okay | |
138 | return Object.class; | |
139 | ||
140 | } else if (type instanceof WildcardType) { | |
141 | return getRawType(((WildcardType) type).getUpperBounds()[0]); | |
142 | ||
143 | } else { | |
144 | String className = type == null ? "null" : type.getClass().getName(); | |
145 | throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " | |
146 | + "GenericArrayType, but <" + type + "> is of type " + className); | |
147 | } | |
148 | } | |
149 | ||
150 | static boolean equal(Object a, Object b) { | |
151 | return a == b || (a != null && a.equals(b)); | |
152 | } | |
153 | ||
154 | /** | |
155 | * Returns true if {@code a} and {@code b} are equal. | |
156 | */ | |
157 | public static boolean equals(Type a, Type b) { | |
158 | if (a == b) { | |
159 | // also handles (a == null && b == null) | |
160 | return true; | |
161 | ||
162 | } else if (a instanceof Class) { | |
163 | // Class already specifies equals(). | |
164 | return a.equals(b); | |
165 | ||
166 | } else if (a instanceof ParameterizedType) { | |
167 | if (!(b instanceof ParameterizedType)) { | |
168 | return false; | |
169 | } | |
170 | ||
171 | // TODO: save a .clone() call | |
172 | ParameterizedType pa = (ParameterizedType) a; | |
173 | ParameterizedType pb = (ParameterizedType) b; | |
174 | return equal(pa.getOwnerType(), pb.getOwnerType()) | |
175 | && pa.getRawType().equals(pb.getRawType()) | |
176 | && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments()); | |
177 | ||
178 | } else if (a instanceof GenericArrayType) { | |
179 | if (!(b instanceof GenericArrayType)) { | |
180 | return false; | |
181 | } | |
182 | ||
183 | GenericArrayType ga = (GenericArrayType) a; | |
184 | GenericArrayType gb = (GenericArrayType) b; | |
185 | return equals(ga.getGenericComponentType(), gb.getGenericComponentType()); | |
186 | ||
187 | } else if (a instanceof WildcardType) { | |
188 | if (!(b instanceof WildcardType)) { | |
189 | return false; | |
190 | } | |
191 | ||
192 | WildcardType wa = (WildcardType) a; | |
193 | WildcardType wb = (WildcardType) b; | |
194 | return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds()) | |
195 | && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds()); | |
196 | ||
197 | } else if (a instanceof TypeVariable) { | |
198 | if (!(b instanceof TypeVariable)) { | |
199 | return false; | |
200 | } | |
201 | TypeVariable<?> va = (TypeVariable<?>) a; | |
202 | TypeVariable<?> vb = (TypeVariable<?>) b; | |
203 | return va.getGenericDeclaration() == vb.getGenericDeclaration() | |
204 | && va.getName().equals(vb.getName()); | |
205 | ||
206 | } else { | |
207 | // This isn't a type we support. Could be a generic array type, wildcard type, etc. | |
208 | return false; | |
209 | } | |
210 | } | |
211 | ||
212 | private static int hashCodeOrZero(Object o) { | |
213 | return o != null ? o.hashCode() : 0; | |
214 | } | |
215 | ||
216 | public static String typeToString(Type type) { | |
217 | return type instanceof Class ? ((Class<?>) type).getName() : type.toString(); | |
218 | } | |
219 | ||
220 | /** | |
221 | * Returns the generic supertype for {@code supertype}. For example, given a class {@code | |
222 | * IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set<Integer>} and the | |
223 | * result when the supertype is {@code Collection.class} is {@code Collection<Integer>}. | |
224 | */ | |
225 | static Type getGenericSupertype(Type context, Class<?> rawType, Class<?> toResolve) { | |
226 | if (toResolve == rawType) { | |
227 | return context; | |
228 | } | |
229 | ||
230 | // we skip searching through interfaces if unknown is an interface | |
231 | if (toResolve.isInterface()) { | |
232 | Class<?>[] interfaces = rawType.getInterfaces(); | |
233 | for (int i = 0, length = interfaces.length; i < length; i++) { | |
234 | if (interfaces[i] == toResolve) { | |
235 | return rawType.getGenericInterfaces()[i]; | |
236 | } else if (toResolve.isAssignableFrom(interfaces[i])) { | |
237 | return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve); | |
238 | } | |
239 | } | |
240 | } | |
241 | ||
242 | // check our supertypes | |
243 | if (!rawType.isInterface()) { | |
244 | while (rawType != Object.class) { | |
245 | Class<?> rawSupertype = rawType.getSuperclass(); | |
246 | if (rawSupertype == toResolve) { | |
247 | return rawType.getGenericSuperclass(); | |
248 | } else if (toResolve.isAssignableFrom(rawSupertype)) { | |
249 | return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve); | |
250 | } | |
251 | rawType = rawSupertype; | |
252 | } | |
253 | } | |
254 | ||
255 | // we can't resolve this further | |
256 | return toResolve; | |
257 | } | |
258 | ||
259 | /** | |
260 | * Returns the generic form of {@code supertype}. For example, if this is {@code | |
261 | * ArrayList<String>}, this returns {@code Iterable<String>} given the input {@code | |
262 | * Iterable.class}. | |
263 | * | |
264 | * @param supertype a superclass of, or interface implemented by, this. | |
265 | */ | |
266 | static Type getSupertype(Type context, Class<?> contextRawType, Class<?> supertype) { | |
267 | checkArgument(supertype.isAssignableFrom(contextRawType)); | |
268 | return resolve(context, contextRawType, | |
269 | $Gson$Types.getGenericSupertype(context, contextRawType, supertype)); | |
270 | } | |
271 | ||
272 | /** | |
273 | * Returns the component type of this array type. | |
274 | * @throws ClassCastException if this type is not an array. | |
275 | */ | |
276 | public static Type getArrayComponentType(Type array) { | |
277 | return array instanceof GenericArrayType | |
278 | ? ((GenericArrayType) array).getGenericComponentType() | |
279 | : ((Class<?>) array).getComponentType(); | |
280 | } | |
281 | ||
282 | /** | |
283 | * Returns the element type of this collection type. | |
284 | * @throws IllegalArgumentException if this type is not a collection. | |
285 | */ | |
286 | public static Type getCollectionElementType(Type context, Class<?> contextRawType) { | |
287 | Type collectionType = getSupertype(context, contextRawType, Collection.class); | |
288 | ||
289 | if (collectionType instanceof WildcardType) { | |
290 | collectionType = ((WildcardType)collectionType).getUpperBounds()[0]; | |
291 | } | |
292 | if (collectionType instanceof ParameterizedType) { | |
293 | return ((ParameterizedType) collectionType).getActualTypeArguments()[0]; | |
294 | } | |
295 | return Object.class; | |
296 | } | |
297 | ||
298 | /** | |
299 | * Returns a two element array containing this map's key and value types in | |
300 | * positions 0 and 1 respectively. | |
301 | */ | |
302 | public static Type[] getMapKeyAndValueTypes(Type context, Class<?> contextRawType) { | |
303 | /* | |
304 | * Work around a problem with the declaration of java.util.Properties. That | |
305 | * class should extend Hashtable<String, String>, but it's declared to | |
306 | * extend Hashtable<Object, Object>. | |
307 | */ | |
308 | if (context == Properties.class) { | |
309 | return new Type[] { String.class, String.class }; // TODO: test subclasses of Properties! | |
310 | } | |
311 | ||
312 | Type mapType = getSupertype(context, contextRawType, Map.class); | |
313 | // TODO: strip wildcards? | |
314 | if (mapType instanceof ParameterizedType) { | |
315 | ParameterizedType mapParameterizedType = (ParameterizedType) mapType; | |
316 | return mapParameterizedType.getActualTypeArguments(); | |
317 | } | |
318 | return new Type[] { Object.class, Object.class }; | |
319 | } | |
320 | ||
321 | public static Type resolve(Type context, Class<?> contextRawType, Type toResolve) { | |
322 | // this implementation is made a little more complicated in an attempt to avoid object-creation | |
323 | while (true) { | |
324 | if (toResolve instanceof TypeVariable) { | |
325 | TypeVariable<?> typeVariable = (TypeVariable<?>) toResolve; | |
326 | toResolve = resolveTypeVariable(context, contextRawType, typeVariable); | |
327 | if (toResolve == typeVariable) { | |
328 | return toResolve; | |
329 | } | |
330 | ||
331 | } else if (toResolve instanceof Class && ((Class<?>) toResolve).isArray()) { | |
332 | Class<?> original = (Class<?>) toResolve; | |
333 | Type componentType = original.getComponentType(); | |
334 | Type newComponentType = resolve(context, contextRawType, componentType); | |
335 | return componentType == newComponentType | |
336 | ? original | |
337 | : arrayOf(newComponentType); | |
338 | ||
339 | } else if (toResolve instanceof GenericArrayType) { | |
340 | GenericArrayType original = (GenericArrayType) toResolve; | |
341 | Type componentType = original.getGenericComponentType(); | |
342 | Type newComponentType = resolve(context, contextRawType, componentType); | |
343 | return componentType == newComponentType | |
344 | ? original | |
345 | : arrayOf(newComponentType); | |
346 | ||
347 | } else if (toResolve instanceof ParameterizedType) { | |
348 | ParameterizedType original = (ParameterizedType) toResolve; | |
349 | Type ownerType = original.getOwnerType(); | |
350 | Type newOwnerType = resolve(context, contextRawType, ownerType); | |
351 | boolean changed = newOwnerType != ownerType; | |
352 | ||
353 | Type[] args = original.getActualTypeArguments(); | |
354 | for (int t = 0, length = args.length; t < length; t++) { | |
355 | Type resolvedTypeArgument = resolve(context, contextRawType, args[t]); | |
356 | if (resolvedTypeArgument != args[t]) { | |
357 | if (!changed) { | |
358 | args = args.clone(); | |
359 | changed = true; | |
360 | } | |
361 | args[t] = resolvedTypeArgument; | |
362 | } | |
363 | } | |
364 | ||
365 | return changed | |
366 | ? newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args) | |
367 | : original; | |
368 | ||
369 | } else if (toResolve instanceof WildcardType) { | |
370 | WildcardType original = (WildcardType) toResolve; | |
371 | Type[] originalLowerBound = original.getLowerBounds(); | |
372 | Type[] originalUpperBound = original.getUpperBounds(); | |
373 | ||
374 | if (originalLowerBound.length == 1) { | |
375 | Type lowerBound = resolve(context, contextRawType, originalLowerBound[0]); | |
376 | if (lowerBound != originalLowerBound[0]) { | |
377 | return supertypeOf(lowerBound); | |
378 | } | |
379 | } else if (originalUpperBound.length == 1) { | |
380 | Type upperBound = resolve(context, contextRawType, originalUpperBound[0]); | |
381 | if (upperBound != originalUpperBound[0]) { | |
382 | return subtypeOf(upperBound); | |
383 | } | |
384 | } | |
385 | return original; | |
386 | ||
387 | } else { | |
388 | return toResolve; | |
389 | } | |
390 | } | |
391 | } | |
392 | ||
393 | static Type resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown) { | |
394 | Class<?> declaredByRaw = declaringClassOf(unknown); | |
395 | ||
396 | // we can't reduce this further | |
397 | if (declaredByRaw == null) { | |
398 | return unknown; | |
399 | } | |
400 | ||
401 | Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw); | |
402 | if (declaredBy instanceof ParameterizedType) { | |
403 | int index = indexOf(declaredByRaw.getTypeParameters(), unknown); | |
404 | return ((ParameterizedType) declaredBy).getActualTypeArguments()[index]; | |
405 | } | |
406 | ||
407 | return unknown; | |
408 | } | |
409 | ||
410 | private static int indexOf(Object[] array, Object toFind) { | |
411 | for (int i = 0; i < array.length; i++) { | |
412 | if (toFind.equals(array[i])) { | |
413 | return i; | |
414 | } | |
415 | } | |
416 | throw new NoSuchElementException(); | |
417 | } | |
418 | ||
419 | /** | |
420 | * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by | |
421 | * a class. | |
422 | */ | |
423 | private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) { | |
424 | GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); | |
425 | return genericDeclaration instanceof Class | |
426 | ? (Class<?>) genericDeclaration | |
427 | : null; | |
428 | } | |
429 | ||
430 | private static void checkNotPrimitive(Type type) { | |
431 | checkArgument(!(type instanceof Class<?>) || !((Class<?>) type).isPrimitive()); | |
432 | } | |
433 | ||
434 | private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable { | |
435 | private final Type ownerType; | |
436 | private final Type rawType; | |
437 | private final Type[] typeArguments; | |
438 | ||
439 | public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { | |
440 | // require an owner type if the raw type needs it | |
441 | if (rawType instanceof Class<?>) { | |
442 | Class<?> rawTypeAsClass = (Class<?>) rawType; | |
443 | checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null); | |
444 | checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null); | |
445 | } | |
446 | ||
447 | this.ownerType = ownerType == null ? null : canonicalize(ownerType); | |
448 | this.rawType = canonicalize(rawType); | |
449 | this.typeArguments = typeArguments.clone(); | |
450 | for (int t = 0; t < this.typeArguments.length; t++) { | |
451 | checkNotNull(this.typeArguments[t]); | |
452 | checkNotPrimitive(this.typeArguments[t]); | |
453 | this.typeArguments[t] = canonicalize(this.typeArguments[t]); | |
454 | } | |
455 | } | |
456 | ||
457 | public Type[] getActualTypeArguments() { | |
458 | return typeArguments.clone(); | |
459 | } | |
460 | ||
461 | public Type getRawType() { | |
462 | return rawType; | |
463 | } | |
464 | ||
465 | public Type getOwnerType() { | |
466 | return ownerType; | |
467 | } | |
468 | ||
469 | @Override public boolean equals(Object other) { | |
470 | return other instanceof ParameterizedType | |
471 | && $Gson$Types.equals(this, (ParameterizedType) other); | |
472 | } | |
473 | ||
474 | @Override public int hashCode() { | |
475 | return Arrays.hashCode(typeArguments) | |
476 | ^ rawType.hashCode() | |
477 | ^ hashCodeOrZero(ownerType); | |
478 | } | |
479 | ||
480 | @Override public String toString() { | |
481 | StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1)); | |
482 | stringBuilder.append(typeToString(rawType)); | |
483 | ||
484 | if (typeArguments.length == 0) { | |
485 | return stringBuilder.toString(); | |
486 | } | |
487 | ||
488 | stringBuilder.append("<").append(typeToString(typeArguments[0])); | |
489 | for (int i = 1; i < typeArguments.length; i++) { | |
490 | stringBuilder.append(", ").append(typeToString(typeArguments[i])); | |
491 | } | |
492 | return stringBuilder.append(">").toString(); | |
493 | } | |
494 | ||
495 | private static final long serialVersionUID = 0; | |
496 | } | |
497 | ||
498 | private static final class GenericArrayTypeImpl implements GenericArrayType, Serializable { | |
499 | private final Type componentType; | |
500 | ||
501 | public GenericArrayTypeImpl(Type componentType) { | |
502 | this.componentType = canonicalize(componentType); | |
503 | } | |
504 | ||
505 | public Type getGenericComponentType() { | |
506 | return componentType; | |
507 | } | |
508 | ||
509 | @Override public boolean equals(Object o) { | |
510 | return o instanceof GenericArrayType | |
511 | && $Gson$Types.equals(this, (GenericArrayType) o); | |
512 | } | |
513 | ||
514 | @Override public int hashCode() { | |
515 | return componentType.hashCode(); | |
516 | } | |
517 | ||
518 | @Override public String toString() { | |
519 | return typeToString(componentType) + "[]"; | |
520 | } | |
521 | ||
522 | private static final long serialVersionUID = 0; | |
523 | } | |
524 | ||
525 | /** | |
526 | * The WildcardType interface supports multiple upper bounds and multiple | |
527 | * lower bounds. We only support what the Java 6 language needs - at most one | |
528 | * bound. If a lower bound is set, the upper bound must be Object.class. | |
529 | */ | |
530 | private static final class WildcardTypeImpl implements WildcardType, Serializable { | |
531 | private final Type upperBound; | |
532 | private final Type lowerBound; | |
533 | ||
534 | public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) { | |
535 | checkArgument(lowerBounds.length <= 1); | |
536 | checkArgument(upperBounds.length == 1); | |
537 | ||
538 | if (lowerBounds.length == 1) { | |
539 | checkNotNull(lowerBounds[0]); | |
540 | checkNotPrimitive(lowerBounds[0]); | |
541 | checkArgument(upperBounds[0] == Object.class); | |
542 | this.lowerBound = canonicalize(lowerBounds[0]); | |
543 | this.upperBound = Object.class; | |
544 | ||
545 | } else { | |
546 | checkNotNull(upperBounds[0]); | |
547 | checkNotPrimitive(upperBounds[0]); | |
548 | this.lowerBound = null; | |
549 | this.upperBound = canonicalize(upperBounds[0]); | |
550 | } | |
551 | } | |
552 | ||
553 | public Type[] getUpperBounds() { | |
554 | return new Type[] { upperBound }; | |
555 | } | |
556 | ||
557 | public Type[] getLowerBounds() { | |
558 | return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY; | |
559 | } | |
560 | ||
561 | @Override public boolean equals(Object other) { | |
562 | return other instanceof WildcardType | |
563 | && $Gson$Types.equals(this, (WildcardType) other); | |
564 | } | |
565 | ||
566 | @Override public int hashCode() { | |
567 | // this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()); | |
568 | return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) | |
569 | ^ (31 + upperBound.hashCode()); | |
570 | } | |
571 | ||
572 | @Override public String toString() { | |
573 | if (lowerBound != null) { | |
574 | return "? super " + typeToString(lowerBound); | |
575 | } else if (upperBound == Object.class) { | |
576 | return "?"; | |
577 | } else { | |
578 | return "? extends " + typeToString(upperBound); | |
579 | } | |
580 | } | |
581 | ||
582 | private static final long serialVersionUID = 0; | |
583 | } | |
584 | } |