下面列出了java.lang.reflect.Executable#getParameterCount ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
private static List<String> getParameterNames(Executable executable)
{
requireNonNull(executable, "executable is null");
if (executable.getParameterCount() == 0) {
return emptyList();
}
// first try to get the parameter names from the ThriftField annotations
List<Optional<String>> parameterNamesFromThriftField = Arrays.stream(executable.getParameters())
.map(ReflectionHelper::getThriftFieldParameterName)
.collect(toImmutableList());
if (parameterNamesFromThriftField.stream().allMatch(Optional::isPresent)) {
return parameterNamesFromThriftField.stream()
.map(Optional::get)
.collect(toImmutableList());
}
// otherwise get the parameter names from the class, but use any ThriftField annotations as overrides
List<String> parameterNamesFromClass = ParameterNames.getParameterNames(executable);
ImmutableList.Builder<String> parameterNames = ImmutableList.builder();
for (int i = 0; i < parameterNamesFromThriftField.size(); i++) {
parameterNames.add(parameterNamesFromThriftField.get(i).orElse(parameterNamesFromClass.get(i)));
}
return parameterNames.build();
}
@Override
public Collection<InjectorResourceHandler<Annotation, Object, ?>> getHandler(Parameter parameter) {
Executable executable = parameter.getDeclaringExecutable();
Annotation[] annotations = fetchAnnotations(parameter);
Collection<InjectorResourceHandler<Annotation, Object, ?>> matched = new ArrayList<>(executable.getParameterCount());
add(matched, new HandlerRecord(parameter.getType(), null));
for (Annotation annotation : annotations) {
add(matched, new HandlerRecord(parameter.getType(), null));
add(matched, new HandlerRecord(parameter.getType(), annotation.annotationType()));
add(matched, new HandlerRecord(null, annotation.annotationType()));
}
return matched;
}
static float calculateDistance(Executable exec, Class<?>[] parameterTypes) {
float cost = 0;
Class<?>[] execTypes = exec.getParameterTypes();
for (int i = 0; i < exec.getParameterCount(); i++) {
if (i >= parameterTypes.length && exec.isVarArgs())
break;
Class<?> a = parameterTypes[i];
Class<?> b = execTypes[i];
if (i == exec.getParameterCount() - 1 && exec.isVarArgs()) {
if (isAssignmentCompatible(a, b)) {
// Passed array for var-args.
cost += calculateDistance(a, b);
} else {
cost += calculateDistance(a, b.getComponentType());
// Penalty for every parameter that wasn't used.
cost += (parameterTypes.length - exec.getParameterCount()) * 3F;
// Death penalty for using var-args.
cost += 10F;
}
} else {
cost += calculateDistance(a, b);
}
}
return cost;
}
static boolean isAssignmentCompatible(Executable exec, Class<?>... parameterTypes) {
// First checks to eliminate an obvious mismatch.
if (exec.isVarArgs()) {
if (exec.getParameterCount() == 1 && parameterTypes.length == 0)
return true;
if (parameterTypes.length < exec.getParameterCount() - 1)
return false;
} else if (parameterTypes.length != exec.getParameterCount()) {
return false;
}
Class<?>[] execTypes = exec.getParameterTypes();
for (int i = 0; i < exec.getParameterCount(); i++) {
Class<?> a = parameterTypes[i];
Class<?> b = execTypes[i];
if (i == exec.getParameterCount() - 1 && exec.isVarArgs()) {
// Passed array type for var-args
if (isAssignmentCompatible(a, b)) {
return true;
}
// Var-args, have to check every element against the array type.
b = b.getComponentType();
for (int j = i; j < parameterTypes.length; j++) {
a = parameterTypes[j];
if (!isAssignmentCompatible(a, b))
return false;
}
return true;
} else if (!isAssignmentCompatible(a, b)) {
return false;
}
}
return true;
}
/**
* Checks whether a method/constructor can be called with the given argument classes. This includes type
* widening and vararg. {@code null} is a wildcard.
*
* <p>E.g., {@code (int.class, int.class)} matches {@code f(Object...), f(int, int), f(Integer, Object)}
* and so forth.
*/
public static boolean isInvokable(Executable executable, Class<?>... classes) {
final int m = executable.getModifiers();
if (!Modifier.isPublic(m)) {
return false;
}
final int paramCount = executable.getParameterCount();
final int classCount = classes.length;
// check for enough classes for each parameter
if (classCount < paramCount || (executable.isVarArgs() && classCount < paramCount - 1)) {
return false;
}
int currentClass = 0;
for (int currentParam = 0; currentParam < paramCount; currentParam++) {
final Class<?> param = executable.getParameterTypes()[currentParam];
// entire parameter matches
if (classes[currentClass] == null || ExtractionUtils.isAssignable(classes[currentClass], param, true)) {
currentClass++;
}
// last parameter is a vararg that consumes remaining classes
else if (currentParam == paramCount - 1 && executable.isVarArgs()) {
final Class<?> paramComponent = executable.getParameterTypes()[currentParam].getComponentType();
while (currentClass < classCount && ExtractionUtils.isAssignable(classes[currentClass], paramComponent, true)) {
currentClass++;
}
}
}
// check if all classes have been consumed
return currentClass == classCount;
}
private static @Nullable List<String> extractExecutableNames(Executable executable) {
final int offset;
if (!Modifier.isStatic(executable.getModifiers())) {
// remove "this" as first parameter
offset = 1;
} else {
offset = 0;
}
// by default parameter names are "arg0, arg1, arg2, ..." if compiler flag is not set
// so we need to extract them manually if possible
List<String> parameterNames = Stream.of(executable.getParameters())
.map(Parameter::getName)
.collect(Collectors.toList());
if (parameterNames.stream().allMatch(n -> n.startsWith("arg"))) {
final ParameterExtractor extractor;
if (executable instanceof Constructor) {
extractor = new ParameterExtractor((Constructor<?>) executable);
} else {
extractor = new ParameterExtractor((Method) executable);
}
getClassReader(executable.getDeclaringClass()).accept(extractor, 0);
final List<String> extractedNames = extractor.getParameterNames();
if (extractedNames.size() == 0) {
return null;
}
// remove "this" and additional local variables
// select less names if class file has not the required information
parameterNames = extractedNames.subList(
offset,
Math.min(executable.getParameterCount() + offset, extractedNames.size()));
}
if (parameterNames.size() != executable.getParameterCount()) {
return null;
}
return parameterNames;
}
private static int validateIndex(Executable executable, int parameterIndex) {
int count = executable.getParameterCount();
Assert.isTrue(parameterIndex >= -1 && parameterIndex < count,
() -> "Parameter index needs to be between -1 and " + (count - 1));
return parameterIndex;
}
private boolean hasOneStringParameter(Executable executable) {
return executable.getParameterCount() == 1 && CharSequence.class.isAssignableFrom(executable.getParameterTypes()[0]);
}
private static int validateIndex(Executable executable, int parameterIndex) {
int count = executable.getParameterCount();
Assert.isTrue(parameterIndex >= -1 && parameterIndex < count,
() -> "Parameter index needs to be between -1 and " + (count - 1));
return parameterIndex;
}
@Override
public boolean test(Executable executable) {
return executable != null && executable.getParameterCount() == count;
}
/**
* Due to a bug in {@code javac} on JDK versions prior to JDK 9, looking up
* annotations directly on a {@link Parameter} will fail for inner class
* constructors.
* <h4>Bug in javac in JDK < 9</h4>
* <p>The parameter annotations array in the compiled byte code excludes an entry
* for the implicit <em>enclosing instance</em> parameter for an inner class
* constructor.
* <h4>Workaround</h4>
* <p>This method provides a workaround for this off-by-one error by allowing the
* caller to access annotations on the preceding {@link Parameter} object (i.e.,
* {@code index - 1}). If the supplied {@code index} is zero, this method returns
* an empty {@code AnnotatedElement}.
* <h4>WARNING</h4>
* <p>The {@code AnnotatedElement} returned by this method should never be cast and
* treated as a {@code Parameter} since the metadata (e.g., {@link Parameter#getName()},
* {@link Parameter#getType()}, etc.) will not match those for the declared parameter
* at the given index in an inner class constructor.
* @return the supplied {@code parameter} or the <em>effective</em> {@code Parameter}
* if the aforementioned bug is in effect
*/
private static AnnotatedElement getEffectiveAnnotatedParameter(Parameter parameter, int index) {
Executable executable = parameter.getDeclaringExecutable();
if (executable instanceof Constructor && ClassUtils.isInnerClass(executable.getDeclaringClass()) &&
executable.getParameterAnnotations().length == executable.getParameterCount() - 1) {
// Bug in javac in JDK <9: annotation array excludes enclosing instance parameter
// for inner classes, so access it with the actual parameter index lowered by 1
return (index == 0 ? EMPTY_ANNOTATED_ELEMENT : executable.getParameters()[index - 1]);
}
return parameter;
}
/**
* Due to a bug in {@code javac} on JDK versions prior to JDK 9, looking up
* annotations directly on a {@link Parameter} will fail for inner class
* constructors.
* <h4>Bug in javac in JDK < 9</h4>
* <p>The parameter annotations array in the compiled byte code excludes an entry
* for the implicit <em>enclosing instance</em> parameter for an inner class
* constructor.
* <h4>Workaround</h4>
* <p>This method provides a workaround for this off-by-one error by allowing the
* caller to access annotations on the preceding {@link Parameter} object (i.e.,
* {@code index - 1}). If the supplied {@code index} is zero, this method returns
* an empty {@code AnnotatedElement}.
* <h4>WARNING</h4>
* <p>The {@code AnnotatedElement} returned by this method should never be cast and
* treated as a {@code Parameter} since the metadata (e.g., {@link Parameter#getName()},
* {@link Parameter#getType()}, etc.) will not match those for the declared parameter
* at the given index in an inner class constructor.
* @return the supplied {@code parameter} or the <em>effective</em> {@code Parameter}
* if the aforementioned bug is in effect
*/
private static AnnotatedElement getEffectiveAnnotatedParameter(Parameter parameter, int index) {
Executable executable = parameter.getDeclaringExecutable();
if (executable instanceof Constructor && ClassUtils.isInnerClass(executable.getDeclaringClass()) &&
executable.getParameterAnnotations().length == executable.getParameterCount() - 1) {
// Bug in javac in JDK <9: annotation array excludes enclosing instance parameter
// for inner classes, so access it with the actual parameter index lowered by 1
return (index == 0 ? EMPTY_ANNOTATED_ELEMENT : executable.getParameters()[index - 1]);
}
return parameter;
}