001/* 002 * Copyright (C) 2012 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 */ 014 015package com.google.common.reflect; 016 017import static com.google.common.base.Preconditions.checkNotNull; 018 019import com.google.common.annotations.Beta; 020import com.google.common.collect.ImmutableList; 021import com.google.errorprone.annotations.CanIgnoreReturnValue; 022import java.lang.annotation.Annotation; 023import java.lang.reflect.AccessibleObject; 024import java.lang.reflect.Constructor; 025import java.lang.reflect.GenericDeclaration; 026import java.lang.reflect.InvocationTargetException; 027import java.lang.reflect.Member; 028import java.lang.reflect.Method; 029import java.lang.reflect.Modifier; 030import java.lang.reflect.Type; 031import java.lang.reflect.TypeVariable; 032import java.util.Arrays; 033import org.checkerframework.checker.nullness.compatqual.NullableDecl; 034 035/** 036 * Wrapper around either a {@link Method} or a {@link Constructor}. Convenience API is provided to 037 * make common reflective operation easier to deal with, such as {@link #isPublic}, {@link 038 * #getParameters} etc. 039 * 040 * <p>In addition to convenience methods, {@link TypeToken#method} and {@link TypeToken#constructor} 041 * will resolve the type parameters of the method or constructor in the context of the owner type, 042 * which may be a subtype of the declaring class. For example: 043 * 044 * <pre>{@code 045 * Method getMethod = List.class.getMethod("get", int.class); 046 * Invokable<List<String>, ?> invokable = new TypeToken<List<String>>() {}.method(getMethod); 047 * assertEquals(TypeToken.of(String.class), invokable.getReturnType()); // Not Object.class! 048 * assertEquals(new TypeToken<List<String>>() {}, invokable.getOwnerType()); 049 * }</pre> 050 * 051 * @param <T> the type that owns this method or constructor. 052 * @param <R> the return type of (or supertype thereof) the method or the declaring type of the 053 * constructor. 054 * @author Ben Yu 055 * @since 14.0 056 */ 057@Beta 058public abstract class Invokable<T, R> extends Element implements GenericDeclaration { 059 060 <M extends AccessibleObject & Member> Invokable(M member) { 061 super(member); 062 } 063 064 /** Returns {@link Invokable} of {@code method}. */ 065 public static Invokable<?, Object> from(Method method) { 066 return new MethodInvokable<>(method); 067 } 068 069 /** Returns {@link Invokable} of {@code constructor}. */ 070 public static <T> Invokable<T, T> from(Constructor<T> constructor) { 071 return new ConstructorInvokable<T>(constructor); 072 } 073 074 /** 075 * Returns {@code true} if this is an overridable method. Constructors, private, static or final 076 * methods, or methods declared by final classes are not overridable. 077 */ 078 public abstract boolean isOverridable(); 079 080 /** Returns {@code true} if this was declared to take a variable number of arguments. */ 081 public abstract boolean isVarArgs(); 082 083 /** 084 * Invokes with {@code receiver} as 'this' and {@code args} passed to the underlying method and 085 * returns the return value; or calls the underlying constructor with {@code args} and returns the 086 * constructed instance. 087 * 088 * @throws IllegalAccessException if this {@code Constructor} object enforces Java language access 089 * control and the underlying method or constructor is inaccessible. 090 * @throws IllegalArgumentException if the number of actual and formal parameters differ; if an 091 * unwrapping conversion for primitive arguments fails; or if, after possible unwrapping, a 092 * parameter value cannot be converted to the corresponding formal parameter type by a method 093 * invocation conversion. 094 * @throws InvocationTargetException if the underlying method or constructor throws an exception. 095 */ 096 // All subclasses are owned by us and we'll make sure to get the R type right. 097 @SuppressWarnings("unchecked") 098 @CanIgnoreReturnValue 099 public final R invoke(@NullableDecl T receiver, Object... args) 100 throws InvocationTargetException, IllegalAccessException { 101 return (R) invokeInternal(receiver, checkNotNull(args)); 102 } 103 104 /** Returns the return type of this {@code Invokable}. */ 105 // All subclasses are owned by us and we'll make sure to get the R type right. 106 @SuppressWarnings("unchecked") 107 public final TypeToken<? extends R> getReturnType() { 108 return (TypeToken<? extends R>) TypeToken.of(getGenericReturnType()); 109 } 110 111 /** 112 * Returns all declared parameters of this {@code Invokable}. Note that if this is a constructor 113 * of a non-static inner class, unlike {@link Constructor#getParameterTypes}, the hidden {@code 114 * this} parameter of the enclosing class is excluded from the returned parameters. 115 */ 116 public final ImmutableList<Parameter> getParameters() { 117 Type[] parameterTypes = getGenericParameterTypes(); 118 Annotation[][] annotations = getParameterAnnotations(); 119 ImmutableList.Builder<Parameter> builder = ImmutableList.builder(); 120 for (int i = 0; i < parameterTypes.length; i++) { 121 builder.add(new Parameter(this, i, TypeToken.of(parameterTypes[i]), annotations[i])); 122 } 123 return builder.build(); 124 } 125 126 /** Returns all declared exception types of this {@code Invokable}. */ 127 public final ImmutableList<TypeToken<? extends Throwable>> getExceptionTypes() { 128 ImmutableList.Builder<TypeToken<? extends Throwable>> builder = ImmutableList.builder(); 129 for (Type type : getGenericExceptionTypes()) { 130 // getGenericExceptionTypes() will never return a type that's not exception 131 @SuppressWarnings("unchecked") 132 TypeToken<? extends Throwable> exceptionType = 133 (TypeToken<? extends Throwable>) TypeToken.of(type); 134 builder.add(exceptionType); 135 } 136 return builder.build(); 137 } 138 139 /** 140 * Explicitly specifies the return type of this {@code Invokable}. For example: 141 * 142 * <pre>{@code 143 * Method factoryMethod = Person.class.getMethod("create"); 144 * Invokable<?, Person> factory = Invokable.of(getNameMethod).returning(Person.class); 145 * }</pre> 146 */ 147 public final <R1 extends R> Invokable<T, R1> returning(Class<R1> returnType) { 148 return returning(TypeToken.of(returnType)); 149 } 150 151 /** Explicitly specifies the return type of this {@code Invokable}. */ 152 public final <R1 extends R> Invokable<T, R1> returning(TypeToken<R1> returnType) { 153 if (!returnType.isSupertypeOf(getReturnType())) { 154 throw new IllegalArgumentException( 155 "Invokable is known to return " + getReturnType() + ", not " + returnType); 156 } 157 @SuppressWarnings("unchecked") // guarded by previous check 158 Invokable<T, R1> specialized = (Invokable<T, R1>) this; 159 return specialized; 160 } 161 162 @SuppressWarnings("unchecked") // The declaring class is T's raw class, or one of its supertypes. 163 @Override 164 public final Class<? super T> getDeclaringClass() { 165 return (Class<? super T>) super.getDeclaringClass(); 166 } 167 168 /** Returns the type of {@code T}. */ 169 // Overridden in TypeToken#method() and TypeToken#constructor() 170 @SuppressWarnings("unchecked") // The declaring class is T. 171 @Override 172 public TypeToken<T> getOwnerType() { 173 return (TypeToken<T>) TypeToken.of(getDeclaringClass()); 174 } 175 176 abstract Object invokeInternal(@NullableDecl Object receiver, Object[] args) 177 throws InvocationTargetException, IllegalAccessException; 178 179 abstract Type[] getGenericParameterTypes(); 180 181 /** This should never return a type that's not a subtype of Throwable. */ 182 abstract Type[] getGenericExceptionTypes(); 183 184 abstract Annotation[][] getParameterAnnotations(); 185 186 abstract Type getGenericReturnType(); 187 188 static class MethodInvokable<T> extends Invokable<T, Object> { 189 190 final Method method; 191 192 MethodInvokable(Method method) { 193 super(method); 194 this.method = method; 195 } 196 197 @Override 198 final Object invokeInternal(@NullableDecl Object receiver, Object[] args) 199 throws InvocationTargetException, IllegalAccessException { 200 return method.invoke(receiver, args); 201 } 202 203 @Override 204 Type getGenericReturnType() { 205 return method.getGenericReturnType(); 206 } 207 208 @Override 209 Type[] getGenericParameterTypes() { 210 return method.getGenericParameterTypes(); 211 } 212 213 @Override 214 Type[] getGenericExceptionTypes() { 215 return method.getGenericExceptionTypes(); 216 } 217 218 @Override 219 final Annotation[][] getParameterAnnotations() { 220 return method.getParameterAnnotations(); 221 } 222 223 @Override 224 public final TypeVariable<?>[] getTypeParameters() { 225 return method.getTypeParameters(); 226 } 227 228 @Override 229 public final boolean isOverridable() { 230 return !(isFinal() 231 || isPrivate() 232 || isStatic() 233 || Modifier.isFinal(getDeclaringClass().getModifiers())); 234 } 235 236 @Override 237 public final boolean isVarArgs() { 238 return method.isVarArgs(); 239 } 240 } 241 242 static class ConstructorInvokable<T> extends Invokable<T, T> { 243 244 final Constructor<?> constructor; 245 246 ConstructorInvokable(Constructor<?> constructor) { 247 super(constructor); 248 this.constructor = constructor; 249 } 250 251 @Override 252 final Object invokeInternal(@NullableDecl Object receiver, Object[] args) 253 throws InvocationTargetException, IllegalAccessException { 254 try { 255 return constructor.newInstance(args); 256 } catch (InstantiationException e) { 257 throw new RuntimeException(constructor + " failed.", e); 258 } 259 } 260 261 /** 262 * If the class is parameterized, such as {@link java.util.ArrayList ArrayList}, this returns 263 * {@code ArrayList<E>}. 264 */ 265 @Override 266 Type getGenericReturnType() { 267 Class<?> declaringClass = getDeclaringClass(); 268 TypeVariable<?>[] typeParams = declaringClass.getTypeParameters(); 269 if (typeParams.length > 0) { 270 return Types.newParameterizedType(declaringClass, typeParams); 271 } else { 272 return declaringClass; 273 } 274 } 275 276 @Override 277 Type[] getGenericParameterTypes() { 278 Type[] types = constructor.getGenericParameterTypes(); 279 if (types.length > 0 && mayNeedHiddenThis()) { 280 Class<?>[] rawParamTypes = constructor.getParameterTypes(); 281 if (types.length == rawParamTypes.length 282 && rawParamTypes[0] == getDeclaringClass().getEnclosingClass()) { 283 // first parameter is the hidden 'this' 284 return Arrays.copyOfRange(types, 1, types.length); 285 } 286 } 287 return types; 288 } 289 290 @Override 291 Type[] getGenericExceptionTypes() { 292 return constructor.getGenericExceptionTypes(); 293 } 294 295 @Override 296 final Annotation[][] getParameterAnnotations() { 297 return constructor.getParameterAnnotations(); 298 } 299 300 /** 301 * {@inheritDoc} 302 * 303 * <p>{@code [<E>]} will be returned for ArrayList's constructor. When both the class and the 304 * constructor have type parameters, the class parameters are prepended before those of the 305 * constructor's. This is an arbitrary rule since no existing language spec mandates one way or 306 * the other. From the declaration syntax, the class type parameter appears first, but the call 307 * syntax may show up in opposite order such as {@code new <A>Foo<B>()}. 308 */ 309 @Override 310 public final TypeVariable<?>[] getTypeParameters() { 311 TypeVariable<?>[] declaredByClass = getDeclaringClass().getTypeParameters(); 312 TypeVariable<?>[] declaredByConstructor = constructor.getTypeParameters(); 313 TypeVariable<?>[] result = 314 new TypeVariable<?>[declaredByClass.length + declaredByConstructor.length]; 315 System.arraycopy(declaredByClass, 0, result, 0, declaredByClass.length); 316 System.arraycopy( 317 declaredByConstructor, 0, result, declaredByClass.length, declaredByConstructor.length); 318 return result; 319 } 320 321 @Override 322 public final boolean isOverridable() { 323 return false; 324 } 325 326 @Override 327 public final boolean isVarArgs() { 328 return constructor.isVarArgs(); 329 } 330 331 private boolean mayNeedHiddenThis() { 332 Class<?> declaringClass = constructor.getDeclaringClass(); 333 if (declaringClass.getEnclosingConstructor() != null) { 334 // Enclosed in a constructor, needs hidden this 335 return true; 336 } 337 Method enclosingMethod = declaringClass.getEnclosingMethod(); 338 if (enclosingMethod != null) { 339 // Enclosed in a method, if it's not static, must need hidden this. 340 return !Modifier.isStatic(enclosingMethod.getModifiers()); 341 } else { 342 // Strictly, this doesn't necessarily indicate a hidden 'this' in the case of 343 // static initializer. But there seems no way to tell in that case. :( 344 // This may cause issues when an anonymous class is created inside a static initializer, 345 // and the class's constructor's first parameter happens to be the enclosing class. 346 // In such case, we may mistakenly think that the class is within a non-static context 347 // and the first parameter is the hidden 'this'. 348 return declaringClass.getEnclosingClass() != null 349 && !Modifier.isStatic(declaringClass.getModifiers()); 350 } 351 } 352 } 353}