• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.reflect;
16 
17 import java.lang.reflect.InvocationHandler;
18 import java.lang.reflect.Method;
19 import java.lang.reflect.Proxy;
20 import java.util.Arrays;
21 import javax.annotation.CheckForNull;
22 import org.checkerframework.checker.nullness.qual.Nullable;
23 
24 /**
25  * Abstract implementation of {@link InvocationHandler} that handles {@link Object#equals}, {@link
26  * Object#hashCode} and {@link Object#toString}. For example:
27  *
28  * <pre>
29  * class Unsupported extends AbstractInvocationHandler {
30  *   protected Object handleInvocation(Object proxy, Method method, Object[] args) {
31  *     throw new UnsupportedOperationException();
32  *   }
33  * }
34  *
35  * CharSequence unsupported = Reflection.newProxy(CharSequence.class, new Unsupported());
36  * </pre>
37  *
38  * @author Ben Yu
39  * @since 12.0
40  */
41 @ElementTypesAreNonnullByDefault
42 public abstract class AbstractInvocationHandler implements InvocationHandler {
43 
44   private static final Object[] NO_ARGS = {};
45 
46   /**
47    * {@inheritDoc}
48    *
49    * <ul>
50    *   <li>{@code proxy.hashCode()} delegates to {@link AbstractInvocationHandler#hashCode}
51    *   <li>{@code proxy.toString()} delegates to {@link AbstractInvocationHandler#toString}
52    *   <li>{@code proxy.equals(argument)} returns true if:
53    *       <ul>
54    *         <li>{@code proxy} and {@code argument} are of the same type
55    *         <li>and {@link AbstractInvocationHandler#equals} returns true for the {@link
56    *             InvocationHandler} of {@code argument}
57    *       </ul>
58    *   <li>other method calls are dispatched to {@link #handleInvocation}.
59    * </ul>
60    */
61   @Override
62   @CheckForNull
invoke(Object proxy, Method method, @CheckForNull @Nullable Object[] args)63   public final Object invoke(Object proxy, Method method, @CheckForNull @Nullable Object[] args)
64       throws Throwable {
65     if (args == null) {
66       args = NO_ARGS;
67     }
68     if (args.length == 0 && method.getName().equals("hashCode")) {
69       return hashCode();
70     }
71     if (args.length == 1
72         && method.getName().equals("equals")
73         && method.getParameterTypes()[0] == Object.class) {
74       Object arg = args[0];
75       if (arg == null) {
76         return false;
77       }
78       if (proxy == arg) {
79         return true;
80       }
81       return isProxyOfSameInterfaces(arg, proxy.getClass())
82           && equals(Proxy.getInvocationHandler(arg));
83     }
84     if (args.length == 0 && method.getName().equals("toString")) {
85       return toString();
86     }
87     return handleInvocation(proxy, method, args);
88   }
89 
90   /**
91    * {@link #invoke} delegates to this method upon any method invocation on the proxy instance,
92    * except {@link Object#equals}, {@link Object#hashCode} and {@link Object#toString}. The result
93    * will be returned as the proxied method's return value.
94    *
95    * <p>Unlike {@link #invoke}, {@code args} will never be null. When the method has no parameter,
96    * an empty array is passed in.
97    */
98   @CheckForNull
handleInvocation(Object proxy, Method method, @Nullable Object[] args)99   protected abstract Object handleInvocation(Object proxy, Method method, @Nullable Object[] args)
100       throws Throwable;
101 
102   /**
103    * By default delegates to {@link Object#equals} so instances are only equal if they are
104    * identical. {@code proxy.equals(argument)} returns true if:
105    *
106    * <ul>
107    *   <li>{@code proxy} and {@code argument} are of the same type
108    *   <li>and this method returns true for the {@link InvocationHandler} of {@code argument}
109    * </ul>
110    *
111    * <p>Subclasses can override this method to provide custom equality.
112    */
113   @Override
equals(@heckForNull Object obj)114   public boolean equals(@CheckForNull Object obj) {
115     return super.equals(obj);
116   }
117 
118   /**
119    * By default delegates to {@link Object#hashCode}. The dynamic proxies' {@code hashCode()} will
120    * delegate to this method. Subclasses can override this method to provide custom equality.
121    */
122   @Override
hashCode()123   public int hashCode() {
124     return super.hashCode();
125   }
126 
127   /**
128    * By default delegates to {@link Object#toString}. The dynamic proxies' {@code toString()} will
129    * delegate to this method. Subclasses can override this method to provide custom string
130    * representation for the proxies.
131    */
132   @Override
toString()133   public String toString() {
134     return super.toString();
135   }
136 
isProxyOfSameInterfaces(Object arg, Class<?> proxyClass)137   private static boolean isProxyOfSameInterfaces(Object arg, Class<?> proxyClass) {
138     return proxyClass.isInstance(arg)
139         // Equal proxy instances should mostly be instance of proxyClass
140         // Under some edge cases (such as the proxy of JDK types serialized and then deserialized)
141         // the proxy type may not be the same.
142         // We first check isProxyClass() so that the common case of comparing with non-proxy objects
143         // is efficient.
144         || (Proxy.isProxyClass(arg.getClass())
145             && Arrays.equals(arg.getClass().getInterfaces(), proxyClass.getInterfaces()));
146   }
147 }
148