Coverage Summary for Class: AbstractInvocationHandler (com.google.common.reflect)
| Class | Class, % | Method, % | Line, % |
|---|---|---|---|
| AbstractInvocationHandler | 100% (1/1) | 57.1% (4/7) | 84% (21/25) |
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 com.google.common.annotations.Beta; 18 import java.lang.reflect.InvocationHandler; 19 import java.lang.reflect.Method; 20 import java.lang.reflect.Proxy; 21 import java.util.Arrays; 22 import javax.annotation.CheckForNull; 23 import org.checkerframework.checker.nullness.qual.Nullable; 24 25 /** 26 * Abstract implementation of {@link InvocationHandler} that handles {@link Object#equals}, {@link 27 * Object#hashCode} and {@link Object#toString}. For example: 28 * 29 * <pre> 30 * class Unsupported extends AbstractInvocationHandler { 31 * protected Object handleInvocation(Object proxy, Method method, Object[] args) { 32 * throw new UnsupportedOperationException(); 33 * } 34 * } 35 * 36 * CharSequence unsupported = Reflection.newProxy(CharSequence.class, new Unsupported()); 37 * </pre> 38 * 39 * @author Ben Yu 40 * @since 12.0 41 */ 42 @Beta 43 // TODO(b/147136275): After adding @Nullable below, add . 44 @ElementTypesAreNonnullByDefault 45 public abstract class AbstractInvocationHandler implements InvocationHandler { 46 47 private static final Object[] NO_ARGS = {}; 48 49 /** 50 * {@inheritDoc} 51 * 52 * <ul> 53 * <li>{@code proxy.hashCode()} delegates to {@link AbstractInvocationHandler#hashCode} 54 * <li>{@code proxy.toString()} delegates to {@link AbstractInvocationHandler#toString} 55 * <li>{@code proxy.equals(argument)} returns true if: 56 * <ul> 57 * <li>{@code proxy} and {@code argument} are of the same type 58 * <li>and {@link AbstractInvocationHandler#equals} returns true for the {@link 59 * InvocationHandler} of {@code argument} 60 * </ul> 61 * <li>other method calls are dispatched to {@link #handleInvocation}. 62 * </ul> 63 */ 64 @Override 65 @CheckForNull 66 public final Object invoke(Object proxy, Method method, @CheckForNull @Nullable Object[] args) 67 throws Throwable { 68 if (args == null) { 69 args = NO_ARGS; 70 } 71 if (args.length == 0 && method.getName().equals("hashCode")) { 72 return hashCode(); 73 } 74 if (args.length == 1 75 && method.getName().equals("equals") 76 && method.getParameterTypes()[0] == Object.class) { 77 Object arg = args[0]; 78 if (arg == null) { 79 return false; 80 } 81 if (proxy == arg) { 82 return true; 83 } 84 return isProxyOfSameInterfaces(arg, proxy.getClass()) 85 && equals(Proxy.getInvocationHandler(arg)); 86 } 87 if (args.length == 0 && method.getName().equals("toString")) { 88 return toString(); 89 } 90 return handleInvocation(proxy, method, args); 91 } 92 93 /** 94 * {@link #invoke} delegates to this method upon any method invocation on the proxy instance, 95 * except {@link Object#equals}, {@link Object#hashCode} and {@link Object#toString}. The result 96 * will be returned as the proxied method's return value. 97 * 98 * <p>Unlike {@link #invoke}, {@code args} will never be null. When the method has no parameter, 99 * an empty array is passed in. 100 */ 101 @CheckForNull 102 protected abstract Object handleInvocation( 103 Object proxy, Method method, /* TODO(b/147136275): Add @Nullable. */ Object[] args) 104 throws Throwable; 105 106 /** 107 * By default delegates to {@link Object#equals} so instances are only equal if they are 108 * identical. {@code proxy.equals(argument)} returns true if: 109 * 110 * <ul> 111 * <li>{@code proxy} and {@code argument} are of the same type 112 * <li>and this method returns true for the {@link InvocationHandler} of {@code argument} 113 * </ul> 114 * 115 * <p>Subclasses can override this method to provide custom equality. 116 */ 117 @Override 118 public boolean equals(@CheckForNull Object obj) { 119 return super.equals(obj); 120 } 121 122 /** 123 * By default delegates to {@link Object#hashCode}. The dynamic proxies' {@code hashCode()} will 124 * delegate to this method. Subclasses can override this method to provide custom equality. 125 */ 126 @Override 127 public int hashCode() { 128 return super.hashCode(); 129 } 130 131 /** 132 * By default delegates to {@link Object#toString}. The dynamic proxies' {@code toString()} will 133 * delegate to this method. Subclasses can override this method to provide custom string 134 * representation for the proxies. 135 */ 136 @Override 137 public String toString() { 138 return super.toString(); 139 } 140 141 private static boolean isProxyOfSameInterfaces(Object arg, Class<?> proxyClass) { 142 return proxyClass.isInstance(arg) 143 // Equal proxy instances should mostly be instance of proxyClass 144 // Under some edge cases (such as the proxy of JDK types serialized and then deserialized) 145 // the proxy type may not be the same. 146 // We first check isProxyClass() so that the common case of comparing with non-proxy objects 147 // is efficient. 148 || (Proxy.isProxyClass(arg.getClass()) 149 && Arrays.equals(arg.getClass().getInterfaces(), proxyClass.getInterfaces())); 150 } 151 }