2010-12-11 6 views
3

私は、equalsおよびhashCodeの反射ヘルパーメソッドを作成しようと考えています。equalsおよびhashCodeの汎用リフレクションヘルパーメソッド

  • helperメソッドは、リフレクションAPIをobjectAのフィールドに照会し、objectBのフィールドと比較します。
  • ハッシュコードの場合、ヘルパーメソッドはフィールドへのリフレクションAPIを調べ、繰り返しループでhashCodeを計算します。

良いことは、私がequalsまたはhashCodeの実装でフィールドがないことを心配してはいけないということです。 悪いことは私がパフォーマンスを推測することです。この考え方についてどう思いますか?あなたの意見をお寄せください!

これは、対等のための私の最初のドラフトです:これはあまりにも高価になるだろう

public final class ReflectiveEqualsHelper { 

public static boolean isEqual(final Object a, final Object b) { 
    if (!isTypeEqual(a, b)) { 
     return false; 
    } 

    Field[] fields = getFields(a); 

    Object valueA; 
    Object valueB; 
    String fieldName; 
    for (int i = 0; i < fields.length; i++) { 
     fieldName = fields[i].getName(); 
     valueA = getValueByFieldName(a, fieldName); 
     valueB = getValueByFieldName(b, fieldName); 
     if (!compare(valueA, valueB)) { 
      return false; 
     } 
    } 
    return true; 
} 

@SuppressWarnings("rawtypes") 
private static Field[] getFields(final Object o) { 
    Class clazz = o.getClass(); 
    Field[] fields = clazz.getDeclaredFields(); 
    return fields; 
} 

private static Field getField(final Object o, final String name) { 
    try { 
     Field field = o.getClass().getDeclaredField(name); 
     return field; 
    } catch (NoSuchFieldException e) { 
     throw new RuntimeException(e); 
    } 
} 

private static Object getValueByFieldName(final Object o, final String name) { 
    Field field = getField(o, name); 
    field.setAccessible(true); 

    try { 
     Object value = field.get(o); 
     field.setAccessible(false); 
     return value; 
    } catch (IllegalAccessException e) { 
     throw new RuntimeException(e); 
    } 

} 

private static boolean areBothNull(final Object a, final Object b) { 
    return (a == null && b == null); 
} 

private static boolean isTypeEqual(final Object a, final Object b) { 
    if (areBothNull(a, b)) { 
     return false; 
    } 

    return a.getClass().equals(b.getClass()); 
} 

private static boolean compare(final Object a, final Object b) { 
    if (a == null) { 
     return false; 
    } else if (b == null) { 
     return false; 
    } 
    return a.equals(b); 
} 

}

public class ReflectiveEqualsHelperTest { 

@Test 
public void testIsEqual() { 
    Vector a = new Vector(Long.valueOf(1L), 3L); 
    Vector b = new Vector(Long.valueOf(1L), 3L); 
    Vector c = new Vector(Long.valueOf(2L), 3L); 
    boolean testA = ReflectiveEqualsHelper.isEqual(a, b); 
    boolean testB = ReflectiveEqualsHelper.isEqual(a, c); 
    boolean testC = ReflectiveEqualsHelper.isEqual(b, c); 
    assertTrue(testA); 
    assertFalse(testB); 
    assertFalse(testC); 
} 

class Vector { 
    public static final int STATIC = 1; 

    private Long x; 
    private long y; 

    public Vector(Long x, long y) { 
     super(); 
     this.x = x; 
     this.y = y; 
    } 

    public Long getX() { 
     return x; 
    } 

    public void setX(Long x) { 
     this.x = x; 
    } 

    public long getY() { 
     return y; 
    } 

    public void setY(long y) { 
     this.y = y; 
    } 

    @Override 
    public int hashCode() { 
     final int prime = 31; 
     int result = 1; 
     result = prime * result + ((x == null) ? 0 : x.hashCode()); 
     result = prime * result + (int) (y^(y >>> 32)); 
     return result; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (this == obj) { 
      return true; 
     } 
     return ReflectiveEqualsHelper.isEqual(this, obj); 
    } 
} 

}

乾杯、ケビン

答えて

2

。これらのメソッドは、期待するよりもはるかに頻繁に呼び出されます。それをしないでください。むしろ、Eclipse、IntelliJ、NetbeansのようなちょっとしたIDEを使って、equals()hashCode()を自動生成させてください。 Eclipseの場合、ソースコード>ソース> Generate hashCodeと等価ののどこかを右クリックすることで実行できます。

alt text

+0

私は日食でそれを行い、それは私にとってはうまく動作します私はちょうどより柔軟な方法について考えていました。 – eglobetrotter

+0

* PITAを試してみて、フィールドを追加または削除するたびにこれらのメソッドを再生成してください。より多くの場合、人々は微妙な、バグを見つけにくいことにつながることを忘れてしまいます。あなたはもちろん、反射的な解決策の費用については正しいです。 –

4

Apache CommonsEqualsBuilderを見て、それがreflectionEquals方法です。このライブラリにはHashCodeBuilder +その他多くの便利なものがあります。

+0

面白いですね、あなたはそのユーティリティクラスの経験がありますか?特に性能とは? – eglobetrotter

+0

はい、私は自分の仕事でプロジェクトに使用します。しかし、パフォーマンスと柔軟性のために(#equalsにはすべてのフィールドが適しているとは限りません)、私は#reflectionEqualsメソッドを使用するのではなく、#appendで "標準的な方法"を使用する傾向があります。確かに、#appendを使った面倒なコーディングがありますが、ボイラープレートが手書きであるかどうかを確認するよりもはるかに便利です:) – Uhlen

+0

GoogleのGuavaにはhashCodeとtoStringヘルパーもあります。equalsの場合、EqualsBuilderを使用します。 equalsを実装している場合は、hasCode実装と一致する必要があることを覚えておいてください。 –

2

パフォーマンスは確かに反射の使用のために大きな懸念ですが、他にもあります。

場合によっては、すべてのフィールドを使用したくない場合もあります。特に自己参照構造の場合、これは潜在的に無限の再帰を導く可能性があります。

+0

再帰は良い点です、ありがとう! – eglobetrotter

3

私はGuava's Objects.hashCodeを提案します。たとえば:

public int hashCode() { 
    return Objects.hashCode(getX(), getY(), getZ()); 
} 

Objects.equals方法は、あなたのisEquals方法の構築を支援すべきです。

+1

...これは 'Arrays.hashCode()'以上のものなので、Guavaをすべて取り込む理由はありません。 –

1

パフォーマンスの問題が原因で反射的アプローチを使用するように心が変わりました。現在、私はApacheコモンズプロジェクトのEqualsBuilderHashCodeBuilderユーティリティを使用しています(提案とフィードバックをお寄せいただきありがとうございます)。 #equalsと方法の迅速な世代のために私は、カスタマイズされたコードテンプレートをFast Code Eclipse pluginを使用します。


<template type="EQUALS_AND_HASHCODE_METHOD"> 
    <variation></variation> 
    <variation-field></variation-field> 
    <allow-multiple-variation></allow-multiple-variation> 
    <class-pattern></class-pattern> 
    <allowed-file-extensions>java</allowed-file-extensions> 
    <number-required-classes>1</number-required-classes> 
    <description>Generates the equals and hashCode method with EqualsBuilder and HashCodeBuilder</description> 
    <template-body> 
    <![CDATA[ 
     @Override 
     public boolean equals(final Object obj) { 
     if (obj == null) { 
      return false; 
     } 
     if (obj == this) { 
      return true; 
     } 
     if (obj.getClass() != getClass()) { 
      return false; 
     } 

     ${class_name} rhs = (${class_name}) obj; 
     return new EqualsBuilder().appendSuper(super.equals(obj)) 
     #foreach ($field in ${fields}) 
      .append(${field.name}, rhs.${field.name}) 
     #end 
      .isEquals(); 
     } 

     @Override 
     public int hashCode() { 
     return new HashCodeBuilder(17, 37).appendSuper(super.hashCode()) 
     #foreach ($field in ${fields}) 
      .append(${field.name}) 
     #end 
      .toHashCode(); 
     } 
    ]]> 
    </template-body> 
</template> 

それが選択したクラスのすべてのフィールドをGRAPすることができますので、私は高速コードプラグインを使用しています。しかし、私はプラグインの使い勝手に満足していません。Eclipseコードのテンプレートエンジンがこれを行うことができればいいと思います。誰かが同様のプラグインを知っているなら、私に教えてください!

乾杯、ケビン

関連する問題