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