1 /*
2 * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package java.lang.reflect;
26
27 import java.lang.annotation.*;
28 import java.util.HashMap;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.Objects;
32 import sun.reflect.annotation.AnnotationSupport;
33
34 /**
35 * Information about method parameters.
36 *
37 * A {@code Parameter} provides information about method parameters,
38 * including its name and modifiers. It also provides an alternate
39 * means of obtaining attributes for the parameter.
40 *
41 * @since 1.8
42 */
43 public final class Parameter implements AnnotatedElement {
44
45 private final String name;
46 private final int modifiers;
47 private final Executable executable;
48 private final int index;
49
50 /**
51 * Package-private constructor for {@code Parameter}.
52 *
53 * If method parameter data is present in the classfile, then the
54 * JVM creates {@code Parameter} objects directly. If it is
55 * absent, however, then {@code Executable} uses this constructor
56 * to synthesize them.
57 *
58 * @param name The name of the parameter, or {@code null} if absent
59 * @param modifiers The modifier flags for the parameter.
60 * @param executable The executable which defines this parameter.
61 * @param index The index of the parameter.
62 */
63 Parameter(String name,
64 int modifiers,
65 Executable executable,
66 int index) {
67 this.name = name;
68 this.modifiers = modifiers;
69 this.executable = executable;
70 this.index = index;
71 }
72
73 /**
74 * Compares based on the executable and the index.
75 *
76 * @param obj The object to compare.
77 * @return Whether or not this is equal to the argument.
78 */
79 @Override
80 public boolean equals(Object obj) {
81 return (obj instanceof Parameter other)
82 && other.executable.equals(executable)
83 && other.index == index;
84 }
85
86 /**
87 * Returns a hash code based on the executable's hash code and the
88 * index.
89 *
90 * @return A hash code based on the executable's hash code.
91 */
92 @Override
93 public int hashCode() {
94 return executable.hashCode() ^ index;
95 }
96
97 /**
98 * Returns true if the parameter has a name according to the class
99 * file; returns false otherwise. Whether a parameter has a name
100 * is determined by the {@literal MethodParameters} attribute of
101 * the method which declares the parameter.
102 *
103 * @return true if and only if the parameter has a name according
104 * to the class file.
105 */
106 public boolean isNamePresent() {
107 return name != null;
108 }
109
110 /**
111 * Returns a string describing this parameter. The format is the
112 * modifiers for the parameter, if any, in canonical order as
113 * recommended by <cite>The Java Language
114 * Specification</cite>, followed by the fully-qualified type of
115 * the parameter (excluding the last [] if the parameter is
116 * variable arity), followed by "..." if the parameter is variable
117 * arity, followed by a space, followed by the name of the
118 * parameter.
119 *
120 * @return A string representation of the parameter and associated
121 * information.
122 */
123 @Override
124 public String toString() {
125 final StringBuilder sb = new StringBuilder();
126 final Type type = getParameterizedType();
127 final String typename = type.getTypeName();
128
129 sb.append(Modifier.toString(getModifiers() & Modifier.parameterModifiers() ));
130
131 if(0 != modifiers)
132 sb.append(' ');
133
134 if(isVarArgs())
135 sb.append(typename.replaceFirst("\\[\\]$", "..."));
136 else
137 sb.append(typename);
138
139 sb.append(' ');
140 sb.append(getName());
141
142 return sb.toString();
143 }
144
145 /**
146 * {@return the {@code Executable} declaring this parameter}
147 */
148 public Executable getDeclaringExecutable() {
149 return executable;
150 }
151
152 /**
153 * {@return the Java language {@linkplain Modifier modifiers} for
154 * the parameter represented by this object}
155 *
156 * @jls 8.4.1 Formal Parameters
157 * @see <a
158 * href="{@docRoot}/java.base/java/lang/reflect/package-summary.html#LanguageJvmModel">Java
159 * programming language and JVM modeling in core reflection</a>
160 */
161 public int getModifiers() {
162 return modifiers;
163 }
164
165 /**
166 * {@return an unmodifiable set of the {@linkplain AccessFlag
167 * access flags} for the parameter represented by this object,
168 * possibly empty}
169 *
170 * @see #getModifiers()
171 * @jvms 4.7.24 The MethodParameters Attribute
172 * @since 20
173 */
174 public Set<AccessFlag> accessFlags() {
175 return AccessibleObject.reflectionFactory.parseAccessFlags(getModifiers(),
176 AccessFlag.Location.METHOD_PARAMETER, getDeclaringExecutable().getDeclaringClass());
177 }
178
179 /**
180 * Returns the name of the parameter. If the parameter's name is
181 * {@linkplain #isNamePresent() present}, then this method returns
182 * the name provided by the class file. Otherwise, this method
183 * synthesizes a name of the form argN, where N is the index of
184 * the parameter in the descriptor of the method which declares
185 * the parameter.
186 *
187 * @return The name of the parameter, either provided by the class
188 * file or synthesized if the class file does not provide
189 * a name.
190 */
191 public String getName() {
192 // Note: empty strings as parameter names are now outlawed.
193 // The .isEmpty() is for compatibility with current JVM
194 // behavior. It may be removed at some point.
195 if(name == null || name.isEmpty())
196 return "arg" + index;
197 else
198 return name;
199 }
200
201 // Package-private accessor to the real name field.
202 String getRealName() {
203 return name;
204 }
205
206 /**
207 * Returns a {@code Type} object that identifies the parameterized
208 * type for the parameter represented by this {@code Parameter}
209 * object.
210 *
211 * @return a {@code Type} object identifying the parameterized
212 * type of the parameter represented by this object
213 */
214 public Type getParameterizedType() {
215 Type tmp = parameterTypeCache;
216 if (null == tmp) {
217 tmp = executable.getAllGenericParameterTypes()[index];
218 parameterTypeCache = tmp;
219 }
220
221 return tmp;
222 }
223
224 private transient volatile Type parameterTypeCache;
225
226 /**
227 * Returns a {@code Class} object that identifies the
228 * declared type for the parameter represented by this
229 * {@code Parameter} object.
230 *
231 * @return a {@code Class} object identifying the declared
232 * type of the parameter represented by this object
233 */
234 public Class<?> getType() {
235 Class<?> tmp = parameterClassCache;
236 if (null == tmp) {
237 tmp = executable.getSharedParameterTypes()[index];
238 parameterClassCache = tmp;
239 }
240 return tmp;
241 }
242
243 /**
244 * Returns an AnnotatedType object that represents the use of a type to
245 * specify the type of the formal parameter represented by this Parameter.
246 *
247 * @return an {@code AnnotatedType} object representing the use of a type
248 * to specify the type of the formal parameter represented by this
249 * Parameter
250 */
251 public AnnotatedType getAnnotatedType() {
252 // no caching for now
253 return executable.getAnnotatedParameterTypes()[index];
254 }
255
256 private transient volatile Class<?> parameterClassCache;
257
258 /**
259 * Returns {@code true} if this parameter is implicitly declared
260 * in source code; returns {@code false} otherwise.
261 *
262 * @return true if and only if this parameter is implicitly
263 * declared as defined by <cite>The Java Language
264 * Specification</cite>.
265 */
266 public boolean isImplicit() {
267 return Modifier.isMandated(getModifiers());
268 }
269
270 /**
271 * Returns {@code true} if this parameter is neither implicitly
272 * nor explicitly declared in source code; returns {@code false}
273 * otherwise.
274 *
275 * @return true if and only if this parameter is a synthetic
276 * construct as defined by
277 * <cite>The Java Language Specification</cite>.
278 * @jls 13.1 The Form of a Binary
279 * @see <a
280 * href="{@docRoot}/java.base/java/lang/reflect/package-summary.html#LanguageJvmModel">Java
281 * programming language and JVM modeling in core reflection</a>
282 */
283 public boolean isSynthetic() {
284 return Modifier.isSynthetic(getModifiers());
285 }
286
287 /**
288 * Returns {@code true} if this parameter represents a variable
289 * argument list; returns {@code false} otherwise.
290 *
291 * @return {@code true} if an only if this parameter represents a
292 * variable argument list.
293 */
294 public boolean isVarArgs() {
295 return executable.isVarArgs() &&
296 index == executable.getParameterCount() - 1;
297 }
298
299
300 /**
301 * {@inheritDoc}
302 * <p>Note that any annotation returned by this method is a
303 * declaration annotation.
304 * @throws NullPointerException {@inheritDoc}
305 */
306 @Override
307 public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
308 Objects.requireNonNull(annotationClass);
309 return annotationClass.cast(declaredAnnotations().get(annotationClass));
310 }
311
312 /**
313 * {@inheritDoc}
314 * <p>Note that any annotations returned by this method are
315 * declaration annotations.
316 *
317 * @throws NullPointerException {@inheritDoc}
318 */
319 @Override
320 public <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
321 Objects.requireNonNull(annotationClass);
322
323 return AnnotationSupport.getDirectlyAndIndirectlyPresent(declaredAnnotations(), annotationClass);
324 }
325
326 /**
327 * {@inheritDoc}
328 * <p>Note that any annotations returned by this method are
329 * declaration annotations.
330 */
331 @Override
332 public Annotation[] getDeclaredAnnotations() {
333 return executable.getParameterAnnotations()[index];
334 }
335
336 /**
337 * {@inheritDoc}
338 * <p>Note that any annotation returned by this method is a
339 * declaration annotation.
340 *
341 * @throws NullPointerException {@inheritDoc}
342 */
343 @Override
344 public <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
345 // Only annotations on classes are inherited, for all other
346 // objects getDeclaredAnnotation is the same as
347 // getAnnotation.
348 return getAnnotation(annotationClass);
349 }
350
351 /**
352 * {@inheritDoc}
353 * <p>Note that any annotations returned by this method are
354 * declaration annotations.
355 *
356 * @throws NullPointerException {@inheritDoc}
357 */
358 @Override
359 public <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
360 // Only annotations on classes are inherited, for all other
361 // objects getDeclaredAnnotations is the same as
362 // getAnnotations.
363 return getAnnotationsByType(annotationClass);
364 }
365
366 /**
367 * {@inheritDoc}
368 * <p>Note that any annotations returned by this method are
369 * declaration annotations.
370 */
371 @Override
372 public Annotation[] getAnnotations() {
373 return getDeclaredAnnotations();
374 }
375
376 private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations;
377
378 private synchronized Map<Class<? extends Annotation>, Annotation> declaredAnnotations() {
379 if(null == declaredAnnotations) {
380 declaredAnnotations = new HashMap<>();
381 for (Annotation a : getDeclaredAnnotations())
382 declaredAnnotations.put(a.annotationType(), a);
383 }
384 return declaredAnnotations;
385 }
386
387 }