17
17
package com .google .gson .reflect ;
18
18
19
19
import static com .google .common .truth .Truth .assertThat ;
20
- import static org .junit .Assert .fail ;
20
+ import static org .junit .Assert .assertThrows ;
21
21
22
22
import java .lang .reflect .GenericArrayType ;
23
+ import java .lang .reflect .ParameterizedType ;
23
24
import java .lang .reflect .Type ;
24
25
import java .util .ArrayList ;
25
26
import java .util .List ;
@@ -103,11 +104,13 @@ public void testArrayFactory() {
103
104
Type listOfString = new TypeToken <List <String >>() {}.getType ();
104
105
assertThat (TypeToken .getArray (listOfString )).isEqualTo (expectedListOfStringArray );
105
106
106
- try {
107
- TypeToken .getArray (null );
108
- fail ();
109
- } catch (NullPointerException e ) {
110
- }
107
+ TypeToken <?> expectedIntArray = new TypeToken <int []>() {};
108
+ assertThat (TypeToken .getArray (int .class )).isEqualTo (expectedIntArray );
109
+
110
+ assertThrows (NullPointerException .class , () -> TypeToken .getArray (null ));
111
+ }
112
+
113
+ static class NestedGeneric <T > {
111
114
}
112
115
113
116
@ Test
@@ -131,84 +134,70 @@ public void testParameterizedFactory() {
131
134
132
135
TypeToken <?> expectedSatisfyingTwoBounds = new TypeToken <GenericWithMultiBound <ClassSatisfyingBounds >>() {};
133
136
assertThat (TypeToken .getParameterized (GenericWithMultiBound .class , ClassSatisfyingBounds .class )).isEqualTo (expectedSatisfyingTwoBounds );
137
+
138
+ TypeToken <?> nestedTypeToken = TypeToken .getParameterized (NestedGeneric .class , Integer .class );
139
+ ParameterizedType nestedParameterizedType = (ParameterizedType ) nestedTypeToken .getType ();
140
+ // TODO: This seems to differ from how Java reflection behaves; when using TypeToken<NestedGeneric<Integer>>,
141
+ // then NestedGeneric<Integer> does have an owner type
142
+ assertThat (nestedParameterizedType .getOwnerType ()).isNull ();
143
+ assertThat (nestedParameterizedType .getRawType ()).isEqualTo (NestedGeneric .class );
144
+ assertThat (nestedParameterizedType .getActualTypeArguments ()).asList ().containsExactly (Integer .class );
145
+
146
+ class LocalGenericClass <T > {}
147
+ TypeToken <?> expectedLocalType = new TypeToken <LocalGenericClass <Integer >>() {};
148
+ assertThat (TypeToken .getParameterized (LocalGenericClass .class , Integer .class )).isEqualTo (expectedLocalType );
134
149
}
135
150
136
151
@ Test
137
152
public void testParameterizedFactory_Invalid () {
138
- try {
139
- TypeToken .getParameterized (null , new Type [0 ]);
140
- fail ();
141
- } catch (NullPointerException e ) {
142
- }
153
+ assertThrows (NullPointerException .class , () -> TypeToken .getParameterized (null , new Type [0 ]));
154
+ assertThrows (NullPointerException .class , () -> TypeToken .getParameterized (List .class , new Type [] { null }));
143
155
144
156
GenericArrayType arrayType = (GenericArrayType ) TypeToken .getArray (String .class ).getType ();
145
- try {
146
- TypeToken .getParameterized (arrayType , new Type [0 ]);
147
- fail ();
148
- } catch (IllegalArgumentException e ) {
149
- assertThat (e ).hasMessageThat ().isEqualTo ("rawType must be of type Class, but was java.lang.String[]" );
150
- }
157
+ IllegalArgumentException e = assertThrows (IllegalArgumentException .class , () -> TypeToken .getParameterized (arrayType , new Type [0 ]));
158
+ assertThat (e ).hasMessageThat ().isEqualTo ("rawType must be of type Class, but was java.lang.String[]" );
151
159
152
- try {
153
- TypeToken .getParameterized (String .class , String .class );
154
- fail ();
155
- } catch (IllegalArgumentException e ) {
156
- assertThat (e ).hasMessageThat ().isEqualTo ("java.lang.String requires 0 type arguments, but got 1" );
157
- }
160
+ e = assertThrows (IllegalArgumentException .class , () -> TypeToken .getParameterized (String .class , Number .class ));
161
+ assertThat (e ).hasMessageThat ().isEqualTo ("java.lang.String is not a generic type" );
158
162
159
- try {
160
- TypeToken .getParameterized (List .class , new Type [0 ]);
161
- fail ();
162
- } catch (IllegalArgumentException e ) {
163
- assertThat (e ).hasMessageThat ().isEqualTo ("java.util.List requires 1 type arguments, but got 0" );
164
- }
163
+ e = assertThrows (IllegalArgumentException .class , () -> TypeToken .getParameterized (List .class , new Type [0 ]));
164
+ assertThat (e ).hasMessageThat ().isEqualTo ("java.util.List requires 1 type arguments, but got 0" );
165
165
166
- try {
167
- TypeToken .getParameterized (List .class , String .class , String .class );
168
- fail ();
169
- } catch (IllegalArgumentException e ) {
170
- assertThat (e ).hasMessageThat ().isEqualTo ("java.util.List requires 1 type arguments, but got 2" );
171
- }
166
+ e = assertThrows (IllegalArgumentException .class , () -> TypeToken .getParameterized (List .class , String .class , String .class ));
167
+ assertThat (e ).hasMessageThat ().isEqualTo ("java.util.List requires 1 type arguments, but got 2" );
172
168
173
- try {
174
- TypeToken .getParameterized (GenericWithBound .class , String .class );
175
- fail ();
176
- } catch (IllegalArgumentException e ) {
177
- assertThat (e ).hasMessageThat ().isEqualTo ("Type argument class java.lang.String does not satisfy bounds "
178
- + "for type variable T declared by " + GenericWithBound .class );
179
- }
169
+ // Primitive types must not be used as type argument
170
+ e = assertThrows (IllegalArgumentException .class , () -> TypeToken .getParameterized (List .class , int .class ));
171
+ assertThat (e ).hasMessageThat ().isEqualTo ("Type argument int does not satisfy bounds"
172
+ + " for type variable E declared by " + List .class );
180
173
181
- try {
182
- TypeToken .getParameterized (GenericWithBound .class , Object .class );
183
- fail ();
184
- } catch (IllegalArgumentException e ) {
185
- assertThat (e ).hasMessageThat ().isEqualTo ("Type argument class java.lang.Object does not satisfy bounds "
186
- + "for type variable T declared by " + GenericWithBound .class );
187
- }
174
+ e = assertThrows (IllegalArgumentException .class , () -> TypeToken .getParameterized (GenericWithBound .class , String .class ));
175
+ assertThat (e ).hasMessageThat ().isEqualTo ("Type argument class java.lang.String does not satisfy bounds"
176
+ + " for type variable T declared by " + GenericWithBound .class );
188
177
189
- try {
190
- TypeToken .getParameterized (GenericWithMultiBound .class , Number .class );
191
- fail ();
192
- } catch (IllegalArgumentException e ) {
193
- assertThat (e ).hasMessageThat ().isEqualTo ("Type argument class java.lang.Number does not satisfy bounds "
194
- + "for type variable T declared by " + GenericWithMultiBound .class );
195
- }
178
+ e = assertThrows (IllegalArgumentException .class , () -> TypeToken .getParameterized (GenericWithBound .class , Object .class ));
179
+ assertThat (e ).hasMessageThat ().isEqualTo ("Type argument class java.lang.Object does not satisfy bounds"
180
+ + " for type variable T declared by " + GenericWithBound .class );
196
181
197
- try {
198
- TypeToken .getParameterized (GenericWithMultiBound .class , CharSequence .class );
199
- fail ();
200
- } catch (IllegalArgumentException e ) {
201
- assertThat (e ).hasMessageThat ().isEqualTo ("Type argument interface java.lang.CharSequence does not satisfy bounds "
202
- + "for type variable T declared by " + GenericWithMultiBound .class );
203
- }
182
+ e = assertThrows (IllegalArgumentException .class , () -> TypeToken .getParameterized (GenericWithMultiBound .class , Number .class ));
183
+ assertThat (e ).hasMessageThat ().isEqualTo ("Type argument class java.lang.Number does not satisfy bounds"
184
+ + " for type variable T declared by " + GenericWithMultiBound .class );
185
+
186
+ e = assertThrows (IllegalArgumentException .class , () -> TypeToken .getParameterized (GenericWithMultiBound .class , CharSequence .class ));
187
+ assertThat (e ).hasMessageThat ().isEqualTo ("Type argument interface java.lang.CharSequence does not satisfy bounds"
188
+ + " for type variable T declared by " + GenericWithMultiBound .class );
189
+
190
+ e = assertThrows (IllegalArgumentException .class , () -> TypeToken .getParameterized (GenericWithMultiBound .class , Object .class ));
191
+ assertThat (e ).hasMessageThat ().isEqualTo ("Type argument class java.lang.Object does not satisfy bounds"
192
+ + " for type variable T declared by " + GenericWithMultiBound .class );
204
193
205
- try {
206
- TypeToken .getParameterized (GenericWithMultiBound .class , Object .class );
207
- fail ();
208
- } catch (IllegalArgumentException e ) {
209
- assertThat (e ).hasMessageThat ().isEqualTo ("Type argument class java.lang.Object does not satisfy bounds "
210
- + "for type variable T declared by " + GenericWithMultiBound .class );
194
+ class Outer {
195
+ class NonStaticInner <T > {}
211
196
}
197
+
198
+ e = assertThrows (IllegalArgumentException .class , () -> TypeToken .getParameterized (Outer .NonStaticInner .class , Object .class ));
199
+ assertThat (e ).hasMessageThat ().isEqualTo ("Raw type " + Outer .NonStaticInner .class .getName ()
200
+ + " is not supported because it requires specifying an owner type" );
212
201
}
213
202
214
203
private static class CustomTypeToken extends TypeToken <String > {
@@ -231,40 +220,23 @@ class SubTypeToken<T> extends TypeToken<String> {}
231
220
class SubSubTypeToken1 <T > extends SubTypeToken <T > {}
232
221
class SubSubTypeToken2 extends SubTypeToken <Integer > {}
233
222
234
- try {
235
- new SubTypeToken <Integer >() {};
236
- fail ();
237
- } catch (IllegalStateException expected ) {
238
- assertThat (expected ).hasMessageThat ().isEqualTo ("Must only create direct subclasses of TypeToken" );
239
- }
223
+ IllegalStateException e = assertThrows (IllegalStateException .class , () -> new SubTypeToken <Integer >() {});
224
+ assertThat (e ).hasMessageThat ().isEqualTo ("Must only create direct subclasses of TypeToken" );
240
225
241
- try {
242
- new SubSubTypeToken1 <Integer >();
243
- fail ();
244
- } catch (IllegalStateException expected ) {
245
- assertThat (expected ).hasMessageThat ().isEqualTo ("Must only create direct subclasses of TypeToken" );
246
- }
226
+ e = assertThrows (IllegalStateException .class , () -> new SubSubTypeToken1 <Integer >());
227
+ assertThat (e ).hasMessageThat ().isEqualTo ("Must only create direct subclasses of TypeToken" );
247
228
248
- try {
249
- new SubSubTypeToken2 ();
250
- fail ();
251
- } catch (IllegalStateException expected ) {
252
- assertThat (expected ).hasMessageThat ().isEqualTo ("Must only create direct subclasses of TypeToken" );
253
- }
229
+ e = assertThrows (IllegalStateException .class , () -> new SubSubTypeToken2 ());
230
+ assertThat (e ).hasMessageThat ().isEqualTo ("Must only create direct subclasses of TypeToken" );
254
231
}
255
232
256
233
@ SuppressWarnings ("rawtypes" )
257
234
@ Test
258
235
public void testTypeTokenRaw () {
259
- try {
260
- new TypeToken () {};
261
- fail ();
262
- } catch (IllegalStateException expected ) {
263
- assertThat (expected ).hasMessageThat ().isEqualTo ("TypeToken must be created with a type argument: new TypeToken<...>() {};"
264
- + " When using code shrinkers (ProGuard, R8, ...) make sure that generic signatures are preserved."
265
- + "\n See https://github.com/google/gson/blob/main/Troubleshooting.md#type-token-raw"
266
- );
267
- }
236
+ IllegalStateException e = assertThrows (IllegalStateException .class , () -> new TypeToken () {});
237
+ assertThat (e ).hasMessageThat ().isEqualTo ("TypeToken must be created with a type argument: new TypeToken<...>() {};"
238
+ + " When using code shrinkers (ProGuard, R8, ...) make sure that generic signatures are preserved."
239
+ + "\n See https://github.com/google/gson/blob/main/Troubleshooting.md#type-token-raw" );
268
240
}
269
241
}
270
242
0 commit comments