1/* 2 * Copyright (C) 2012 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.common.reflect; 18 19import static com.google.common.base.Preconditions.checkNotNull; 20 21import com.google.common.annotations.Beta; 22import com.google.common.collect.ImmutableList; 23 24import java.lang.annotation.Annotation; 25import java.lang.reflect.AccessibleObject; 26import java.lang.reflect.Constructor; 27import java.lang.reflect.GenericDeclaration; 28import java.lang.reflect.InvocationTargetException; 29import java.lang.reflect.Member; 30import java.lang.reflect.Method; 31import java.lang.reflect.Modifier; 32import java.lang.reflect.Type; 33import java.lang.reflect.TypeVariable; 34import java.util.Arrays; 35 36import javax.annotation.Nullable; 37 38/** 39 * Wrapper around either a {@link Method} or a {@link Constructor}. 40 * Convenience API is provided to make common reflective operation easier to deal with, 41 * such as {@link #isPublic}, {@link #getParameters} etc. 42 * 43 * <p>In addition to convenience methods, {@link TypeToken#method} and {@link 44 * TypeToken#constructor} will resolve the type parameters of the method or constructor in the 45 * context of the owner type, which may be a subtype of the declaring class. For example: 46 * 47 * <pre> {@code 48 * Method getMethod = List.class.getMethod("get", int.class); 49 * Invokable<List<String>, ?> invokable = new TypeToken<List<String>>() {}.method(getMethod); 50 * assertEquals(TypeToken.of(String.class), invokable.getReturnType()); // Not Object.class! 51 * assertEquals(new TypeToken<List<String>>() {}, invokable.getOwnerType());}</pre> 52 * 53 * @param <T> the type that owns this method or constructor. 54 * @param <R> the return type of (or supertype thereof) the method or the declaring type of the 55 * constructor. 56 * @author Ben Yu 57 * @since 14.0 58 */ 59@Beta 60public abstract class Invokable<T, R> extends Element implements GenericDeclaration { 61 62 <M extends AccessibleObject & Member> Invokable(M member) { 63 super(member); 64 } 65 66 /** Returns {@link Invokable} of {@code method}. */ 67 public static Invokable<?, Object> from(Method method) { 68 return new MethodInvokable<Object>(method); 69 } 70 71 /** Returns {@link Invokable} of {@code constructor}. */ 72 public static <T> Invokable<T, T> from(Constructor<T> constructor) { 73 return new ConstructorInvokable<T>(constructor); 74 } 75 76 /** 77 * Returns {@code true} if this is an overridable method. Constructors, private, static or final 78 * methods, or methods declared by final classes are not overridable. 79 */ 80 public abstract boolean isOverridable(); 81 82 /** Returns {@code true} if this was declared to take a variable number of arguments. */ 83 public abstract boolean isVarArgs(); 84 85 /** 86 * Invokes with {@code receiver} as 'this' and {@code args} passed to the underlying method 87 * and returns the return value; or calls the underlying constructor with {@code args} and returns 88 * the constructed instance. 89 * 90 * @throws IllegalAccessException if this {@code Constructor} object enforces Java language 91 * access control and the underlying method or constructor is inaccessible. 92 * @throws IllegalArgumentException if the number of actual and formal parameters differ; 93 * if an unwrapping conversion for primitive arguments fails; or if, after possible 94 * unwrapping, a parameter value cannot be converted to the corresponding formal 95 * parameter type by a method invocation conversion. 96 * @throws InvocationTargetException if the underlying method or constructor throws an exception. 97 */ 98 // All subclasses are owned by us and we'll make sure to get the R type right. 99 @SuppressWarnings("unchecked") 100 public final R invoke(@Nullable T receiver, Object... args) 101 throws InvocationTargetException, IllegalAccessException { 102 return (R) invokeInternal(receiver, checkNotNull(args)); 103 } 104 105 /** Returns the return type of this {@code Invokable}. */ 106 // All subclasses are owned by us and we'll make sure to get the R type right. 107 @SuppressWarnings("unchecked") 108 public final TypeToken<? extends R> getReturnType() { 109 return (TypeToken<? extends R>) TypeToken.of(getGenericReturnType()); 110 } 111 112 /** 113 * Returns all declared parameters of this {@code Invokable}. Note that if this is a constructor 114 * of a non-static inner class, unlike {@link Constructor#getParameterTypes}, the hidden 115 * {@code this} parameter of the enclosing class is excluded from the returned parameters. 116 */ 117 public final ImmutableList<Parameter> getParameters() { 118 Type[] parameterTypes = getGenericParameterTypes(); 119 Annotation[][] annotations = getParameterAnnotations(); 120 ImmutableList.Builder<Parameter> builder = ImmutableList.builder(); 121 for (int i = 0; i < parameterTypes.length; i++) { 122 builder.add(new Parameter( 123 this, i, TypeToken.of(parameterTypes[i]), annotations[i])); 124 } 125 return builder.build(); 126 } 127 128 /** Returns all declared exception types of this {@code Invokable}. */ 129 public final ImmutableList<TypeToken<? extends Throwable>> getExceptionTypes() { 130 ImmutableList.Builder<TypeToken<? extends Throwable>> builder = ImmutableList.builder(); 131 for (Type type : getGenericExceptionTypes()) { 132 // getGenericExceptionTypes() will never return a type that's not exception 133 @SuppressWarnings("unchecked") 134 TypeToken<? extends Throwable> exceptionType = (TypeToken<? extends Throwable>) 135 TypeToken.of(type); 136 builder.add(exceptionType); 137 } 138 return builder.build(); 139 } 140 141 /** 142 * Explicitly specifies the return type of this {@code Invokable}. For example: 143 * <pre> {@code 144 * Method factoryMethod = Person.class.getMethod("create"); 145 * Invokable<?, Person> factory = Invokable.of(getNameMethod).returning(Person.class);}</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.isAssignableFrom(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 public final Class<? super T> getDeclaringClass() { 164 return (Class<? super T>) super.getDeclaringClass(); 165 } 166 167 /** Returns the type of {@code T}. */ 168 // Overridden in TypeToken#method() and TypeToken#constructor() 169 @SuppressWarnings("unchecked") // The declaring class is T. 170 @Override public TypeToken<T> getOwnerType() { 171 return (TypeToken<T>) TypeToken.of(getDeclaringClass()); 172 } 173 174 abstract Object invokeInternal(@Nullable Object receiver, Object[] args) 175 throws InvocationTargetException, IllegalAccessException; 176 177 abstract Type[] getGenericParameterTypes(); 178 179 /** This should never return a type that's not a subtype of Throwable. */ 180 abstract Type[] getGenericExceptionTypes(); 181 182 abstract Annotation[][] getParameterAnnotations(); 183 184 abstract Type getGenericReturnType(); 185 186 static class MethodInvokable<T> extends Invokable<T, Object> { 187 188 final Method method; 189 190 MethodInvokable(Method method) { 191 super(method); 192 this.method = method; 193 } 194 195 @Override final Object invokeInternal(@Nullable Object receiver, Object[] args) 196 throws InvocationTargetException, IllegalAccessException { 197 return method.invoke(receiver, args); 198 } 199 200 @Override Type getGenericReturnType() { 201 return method.getGenericReturnType(); 202 } 203 204 @Override Type[] getGenericParameterTypes() { 205 return method.getGenericParameterTypes(); 206 } 207 208 @Override Type[] getGenericExceptionTypes() { 209 return method.getGenericExceptionTypes(); 210 } 211 212 @Override final Annotation[][] getParameterAnnotations() { 213 return method.getParameterAnnotations(); 214 } 215 216 @Override public final TypeVariable<?>[] getTypeParameters() { 217 return method.getTypeParameters(); 218 } 219 220 @Override public final boolean isOverridable() { 221 return !(isFinal() || isPrivate() || isStatic() 222 || Modifier.isFinal(getDeclaringClass().getModifiers())); 223 } 224 225 @Override public final boolean isVarArgs() { 226 return method.isVarArgs(); 227 } 228 } 229 230 static class ConstructorInvokable<T> extends Invokable<T, T> { 231 232 final Constructor<?> constructor; 233 234 ConstructorInvokable(Constructor<?> constructor) { 235 super(constructor); 236 this.constructor = constructor; 237 } 238 239 @Override final Object invokeInternal(@Nullable Object receiver, Object[] args) 240 throws InvocationTargetException, IllegalAccessException { 241 try { 242 return constructor.newInstance(args); 243 } catch (InstantiationException e) { 244 throw new RuntimeException(constructor + " failed.", e); 245 } 246 } 247 248 /** If the class is parameterized, such as ArrayList, this returns ArrayList<E>. */ 249 @Override Type getGenericReturnType() { 250 Class<?> declaringClass = getDeclaringClass(); 251 TypeVariable<?>[] typeParams = declaringClass.getTypeParameters(); 252 if (typeParams.length > 0) { 253 return Types.newParameterizedType(declaringClass, typeParams); 254 } else { 255 return declaringClass; 256 } 257 } 258 259 @Override Type[] getGenericParameterTypes() { 260 Type[] types = constructor.getGenericParameterTypes(); 261 if (types.length > 0 && mayNeedHiddenThis()) { 262 Class<?>[] rawParamTypes = constructor.getParameterTypes(); 263 if (types.length == rawParamTypes.length 264 && rawParamTypes[0] == getDeclaringClass().getEnclosingClass()) { 265 // first parameter is the hidden 'this' 266 return Arrays.copyOfRange(types, 1, types.length); 267 } 268 } 269 return types; 270 } 271 272 @Override Type[] getGenericExceptionTypes() { 273 return constructor.getGenericExceptionTypes(); 274 } 275 276 @Override final Annotation[][] getParameterAnnotations() { 277 return constructor.getParameterAnnotations(); 278 } 279 280 /** 281 * {@inheritDoc} 282 * 283 * {@code [<E>]} will be returned for ArrayList's constructor. When both the class and the 284 * constructor have type parameters, the class parameters are prepended before those of the 285 * constructor's. This is an arbitrary rule since no existing language spec mandates one way or 286 * the other. From the declaration syntax, the class type parameter appears first, but the 287 * call syntax may show up in opposite order such as {@code new <A>Foo<B>()}. 288 */ 289 @Override public final TypeVariable<?>[] getTypeParameters() { 290 TypeVariable<?>[] declaredByClass = getDeclaringClass().getTypeParameters(); 291 TypeVariable<?>[] declaredByConstructor = constructor.getTypeParameters(); 292 TypeVariable<?>[] result = 293 new TypeVariable<?>[declaredByClass.length + declaredByConstructor.length]; 294 System.arraycopy(declaredByClass, 0, result, 0, declaredByClass.length); 295 System.arraycopy( 296 declaredByConstructor, 0, 297 result, declaredByClass.length, 298 declaredByConstructor.length); 299 return result; 300 } 301 302 @Override public final boolean isOverridable() { 303 return false; 304 } 305 306 @Override public final boolean isVarArgs() { 307 return constructor.isVarArgs(); 308 } 309 310 private boolean mayNeedHiddenThis() { 311 Class<?> declaringClass = constructor.getDeclaringClass(); 312 if (declaringClass.getEnclosingConstructor() != null) { 313 // Enclosed in a constructor, needs hidden this 314 return true; 315 } 316 Method enclosingMethod = declaringClass.getEnclosingMethod(); 317 if (enclosingMethod != null) { 318 // Enclosed in a method, if it's not static, must need hidden this. 319 return !Modifier.isStatic(enclosingMethod.getModifiers()); 320 } else { 321 // Strictly, this doesn't necessarily indicate a hidden 'this' in the case of 322 // static initializer. But there seems no way to tell in that case. :( 323 // This may cause issues when an anonymous class is created inside a static initializer, 324 // and the class's constructor's first parameter happens to be the enclosing class. 325 // In such case, we may mistakenly think that the class is within a non-static context 326 // and the first parameter is the hidden 'this'. 327 return declaringClass.getEnclosingClass() != null 328 && !Modifier.isStatic(declaringClass.getModifiers()); 329 } 330 } 331 } 332} 333