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