001/* 002 * Copyright (C) 2012 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.reflect; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020 021import com.google.common.annotations.Beta; 022import com.google.common.collect.ImmutableList; 023 024import java.lang.annotation.Annotation; 025import java.lang.reflect.AccessibleObject; 026import java.lang.reflect.Constructor; 027import java.lang.reflect.GenericDeclaration; 028import java.lang.reflect.InvocationTargetException; 029import java.lang.reflect.Member; 030import java.lang.reflect.Method; 031import java.lang.reflect.Modifier; 032import java.lang.reflect.Type; 033import java.lang.reflect.TypeVariable; 034import java.util.Arrays; 035 036import javax.annotation.Nullable; 037 038/** 039 * Wrapper around either a {@link Method} or a {@link Constructor}. 040 * Convenience API is provided to make common reflective operation easier to deal with, 041 * such as {@link #isPublic}, {@link #getParameters} etc. 042 * 043 * <p>In addition to convenience methods, {@link TypeToken#method} and {@link 044 * TypeToken#constructor} will resolve the type parameters of the method or constructor in the 045 * context of the owner type, which may be a subtype of the declaring class. For example: 046 * <pre> {@code 047 * 048 * Method getMethod = List.class.getMethod("get", int.class); 049 * Invokable<List<String>, ?> invokable = new TypeToken<List<String>>() {}.method(getMethod); 050 * assertEquals(TypeToken.of(String.class), invokable.getReturnType()); // Not Object.class! 051 * assertEquals(new TypeToken<List<String>>() {}, invokable.getOwnerType());}</pre> 052 * 053 * @param <T> the type that owns this method or constructor. 054 * @param <R> the return type of (or supertype thereof) the method or the declaring type of the 055 * constructor. 056 * @author Ben Yu 057 * @since 14.0 058 */ 059@Beta 060public abstract class Invokable<T, R> extends Element implements GenericDeclaration { 061 062 <M extends AccessibleObject & Member> Invokable(M member) { 063 super(member); 064 } 065 066 /** Returns {@link Invokable} of {@code method}. */ 067 public static Invokable<?, Object> from(Method method) { 068 return new MethodInvokable<Object>(method); 069 } 070 071 /** Returns {@link Invokable} of {@code constructor}. */ 072 public static <T> Invokable<T, T> from(Constructor<T> constructor) { 073 return new ConstructorInvokable<T>(constructor); 074 } 075 076 /** 077 * Returns {@code true} if this is an overridable method. Constructors, private, static or final 078 * methods, or methods declared by final classes are not overridable. 079 */ 080 public abstract boolean isOverridable(); 081 082 /** Returns {@code true} if this was declared to take a variable number of arguments. */ 083 public abstract boolean isVarArgs(); 084 085 /** 086 * Invokes with {@code receiver} as 'this' and {@code args} passed to the underlying method 087 * and returns the return value; or calls the underlying constructor with {@code args} and returns 088 * the constructed instance. 089 * 090 * @throws IllegalAccessException if this {@code Constructor} object enforces Java language 091 * access control and the underlying method or constructor is inaccessible. 092 * @throws IllegalArgumentException if the number of actual and formal parameters differ; 093 * if an unwrapping conversion for primitive arguments fails; or if, after possible 094 * unwrapping, a parameter value cannot be converted to the corresponding formal 095 * parameter type by a method invocation conversion. 096 * @throws InvocationTargetException if the underlying method or constructor throws an exception. 097 */ 098 // All subclasses are owned by us and we'll make sure to get the R type right. 099 @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); 146 * }</pre> 147 */ 148 public final <R1 extends R> Invokable<T, R1> returning(Class<R1> returnType) { 149 return returning(TypeToken.of(returnType)); 150 } 151 152 /** Explicitly specifies the return type of this {@code Invokable}. */ 153 public final <R1 extends R> Invokable<T, R1> returning(TypeToken<R1> returnType) { 154 if (!returnType.isAssignableFrom(getReturnType())) { 155 throw new IllegalArgumentException( 156 "Invokable is known to return " + getReturnType() + ", not " + returnType); 157 } 158 @SuppressWarnings("unchecked") // guarded by previous check 159 Invokable<T, R1> specialized = (Invokable<T, R1>) this; 160 return specialized; 161 } 162 163 @SuppressWarnings("unchecked") // The declaring class is T's raw class, or one of its supertypes. 164 @Override 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 public TypeToken<T> getOwnerType() { 172 return (TypeToken<T>) TypeToken.of(getDeclaringClass()); 173 } 174 175 abstract Object invokeInternal(@Nullable Object receiver, Object[] args) 176 throws InvocationTargetException, IllegalAccessException; 177 178 abstract Type[] getGenericParameterTypes(); 179 180 /** This should never return a type that's not a subtype of Throwable. */ 181 abstract Type[] getGenericExceptionTypes(); 182 183 abstract Annotation[][] getParameterAnnotations(); 184 185 abstract Type getGenericReturnType(); 186 187 static class MethodInvokable<T> extends Invokable<T, Object> { 188 189 private final Method method; 190 191 MethodInvokable(Method method) { 192 super(method); 193 this.method = method; 194 } 195 196 @Override final Object invokeInternal(@Nullable Object receiver, Object[] args) 197 throws InvocationTargetException, IllegalAccessException { 198 return method.invoke(receiver, args); 199 } 200 201 @Override Type getGenericReturnType() { 202 return method.getGenericReturnType(); 203 } 204 205 @Override Type[] getGenericParameterTypes() { 206 return method.getGenericParameterTypes(); 207 } 208 209 @Override Type[] getGenericExceptionTypes() { 210 return method.getGenericExceptionTypes(); 211 } 212 213 @Override final Annotation[][] getParameterAnnotations() { 214 return method.getParameterAnnotations(); 215 } 216 217 @Override public final TypeVariable<?>[] getTypeParameters() { 218 return method.getTypeParameters(); 219 } 220 221 @Override public final boolean isOverridable() { 222 return !(isFinal() || isPrivate() || isStatic() 223 || Modifier.isFinal(getDeclaringClass().getModifiers())); 224 } 225 226 @Override public final boolean isVarArgs() { 227 return method.isVarArgs(); 228 } 229 } 230 231 static class ConstructorInvokable<T> extends Invokable<T, T> { 232 233 private final Constructor<?> constructor; 234 235 ConstructorInvokable(Constructor<?> constructor) { 236 super(constructor); 237 this.constructor = constructor; 238 } 239 240 @Override final Object invokeInternal(@Nullable Object receiver, Object[] args) 241 throws InvocationTargetException, IllegalAccessException { 242 try { 243 return constructor.newInstance(args); 244 } catch (InstantiationException e) { 245 throw new RuntimeException(constructor + " failed.", e); 246 } 247 } 248 249 @Override Type getGenericReturnType() { 250 return constructor.getDeclaringClass(); 251 } 252 253 @Override Type[] getGenericParameterTypes() { 254 Type[] types = constructor.getGenericParameterTypes(); 255 Class<?> declaringClass = constructor.getDeclaringClass(); 256 if (!Modifier.isStatic(declaringClass.getModifiers()) 257 && declaringClass.getEnclosingClass() != null) { 258 if (types.length == constructor.getParameterTypes().length) { 259 // first parameter is the hidden 'this' 260 return Arrays.copyOfRange(types, 1, types.length); 261 } 262 } 263 return types; 264 } 265 266 @Override Type[] getGenericExceptionTypes() { 267 return constructor.getGenericExceptionTypes(); 268 } 269 270 @Override final Annotation[][] getParameterAnnotations() { 271 return constructor.getParameterAnnotations(); 272 } 273 274 @Override public final TypeVariable<?>[] getTypeParameters() { 275 return constructor.getTypeParameters(); 276 } 277 278 @Override public final boolean isOverridable() { 279 return false; 280 } 281 282 @Override public final boolean isVarArgs() { 283 return constructor.isVarArgs(); 284 } 285 } 286}