2016-10-22 125 views
0

我需要找到一種方法,以這樣的方式來緩存方法(java.lang.reflect.Method),每當我調用一個函數與類(Class)方法名(String)和參數(T[])功能將返回緩存的方法(如果存在)或找到該方法,將其添加到緩存並返回。重寫的hashCode equals方法

我想用HashMap緩存,以便我能找到在O方法(1),但問題是,我需要使用isAssignableFrom當我覆蓋了equals方法:

public class A1 extends AParent {} 

public class A2 extends AParent {} 

public class AParent {} 

public class Temp{ 
    public void testFunc(AParent a){} 
} 

這是類我在HashMap中使用的鍵:

import java.util.Arrays; 

class MethodAbs{ 
Class c; 
String methodName; 
Class<?>[] argsTypes; 

public MethodAbs(Class c, String methodName, Class<?>[] argsTypes){ 
    this.c = c; 
    this.methodName = methodName; 
    this.argsTypes = argsTypes; 
} 

@Override 
public boolean equals(Object o) { 
    if (this == o) return true; 
    if (o == null || getClass() != o.getClass()) return false; 

    MethodAbs methodAbs = (MethodAbs) o; 

    if (c != null ? !c.equals(methodAbs.c) : methodAbs.c != null) return false; 
    if (methodName != null ? !methodName.equals(methodAbs.methodName) : methodAbs.methodName != null) 
     return false; 
    return isArgsTypesEq(argsTypes, methodAbs.argsTypes); 

} 

//a method is equals to the one cached if the arguments types 
// can be cast to the ones that are saved on the map, 
// i.e the ones on the method declaration 

private boolean isArgsTypesEq(Class<?>[] at1, Class<?>[] at2){ 
    boolean res = at1.length == at2.length; 
    for(int i = 0; i<at1.length && res; i++){ 
     if(!at1[i].isAssignableFrom(at2[i])) res = false; 
    } 
    return res; 
} 


//default implementation (not working properly!) 

@Override 
public int hashCode() { 
    int result = c != null ? c.hashCode() : 0; 
    result = 31 * result + (methodName != null ? methodName.hashCode() : 0); 
    result = 31 * result + Arrays.hashCode(argsTypes); 
    return result; 
} 


} 

我使用的緩存類

class Run{ 

public Map<MethodAbs, Method> map = new HashMap<>(); 

public<T> Method myFunc(Class c, String methodName, T[] args){ 
    MethodAbs ma = new MethodAbs(c, methodName, getTypes(args)); 
    if(map.containsKey(ma)){ 
     return map.get(ma); 
    } 
    else{ 
     for(Method method: c.getMethods()){ 
      MethodAbs currMethodAbs = new MethodAbs(c, method.getName(), method.getParameterTypes()); 
      if(!map.containsKey(currMethodAbs)) 
       map.put(currMethodAbs, method); 
      if(currMethodAbs.equals(ma)) break; 
     } 
    } 
    return map.get(ma); 
} 

private<T> Class<?>[] getTypes(T[] args) { 
    Class<?>[] types = new Class<?>[args.length]; 
    for(int i = 0; i< args.length; i++){ 
     types[i] = args[i].getClass(); 
    } 
    return types; 
} 
} 

而且主營:

public static void main(String[] args){ 
    Run r = new Run(); 
    Object [] arr = new Object[1]; 
    arr[0] = new A1(); 
    r.myFunc(Temp.class, "testFunc", arr); 
    arr[0] = new A2(); 
    r.myFunc(Temp.class, "testFunc", arr); 

} 

在該方案中調用r.myFunc首次上面後,地圖上看起來是這樣的:

MethodAbs(Temp.class, "testFunc", [AParent.class]) 
在第二時間map.containsKey

將返回false(因爲AParent.hashCode!= A2.hashCode),但他們是equals

  • 在示例中所示的層次結構不一定會看起來像(例如A2可以是AParent的孫)

我知道可以使用類和方法名作爲關鍵字和值將是我需要迭代和比較平等的方法列表,但我試圖找到一個更好的方法...

回答

0

不幸的是,你是equals方法是從根本上打破,至少有兩個原因。

  1. 它不是對稱的,請看下面的代碼片段:

    public static void main(String... args) { 
        MethodAbs methodValueOfObject = new MethodAbs(String.class, "valueOf", new Class<?>[] { Object.class }); 
        MethodAbs methodValueOfCharArrays = new MethodAbs(String.class, "valueOf", new Class<?>[] { char[].class }); 
        System.out.println(methodValueOfObject.equals(methodValueOfCharArrays)); // prints "true" 
        System.out.println(methodValueOfCharArrays.equals(methodValueOfObject)); // prints "false" 
    } 
    
  2. 它相當於你可能並不意味着被視爲相等的方法。想象一下,您的Temp類有兩個testFunc方法,public void testFunc(A1 a)public void testFunc(A2 a)。相應的對象不應該是平等的,但根據你的實施,他們確實是。

我認爲最好的解決方案是完全擺脫緩存。只需使用

public Method getMethod(Class<?> c, String methodName, Class<?>... paramClasses) { 
    try { 
     return c.getDeclaredMethod(methodName, paramClasses); 
    } catch (NoSuchMethodException | SecurityException e) { 
     // Your exception handling goes here 
     return null; 
    } 
} 

Class對象已經由類加載器的緩存,因此性能損失可以忽略不計。

+0

我同意破損的對稱性問題,但是我不能使用getDecleredMethod,因爲它期望接收方法的確切對象類型。在上面的例子中用Class A1調用它會拋出一個異常,因爲它正在尋找類AParent。任何想法如何解決這個問題? –

相關問題