equals()
( javadoc) должен определить отношение эквивалентности (это должно быть рефлексивно , симметричный , и переходный ). Кроме того, это должно быть последовательно (если объекты не изменяются, то это должно продолжать возвращать то же значение). Кроме того, o.equals(null)
должен всегда возвращать false.
hashCode()
( javadoc) должен также быть последовательны (если объект не изменяется с точки зрения equals()
, это должно продолжать возвращать то же значение).
отношение между этими двумя методами:
Каждый раз, когда
a.equals(b)
, тогдаa.hashCode()
должно быть то же какb.hashCode()
.
, Если Вы переопределяете один, тогда необходимо переопределить другой.
Использование тот же набор полей, которые Вы используете для вычисления equals()
для вычисления hashCode()
.
Использование превосходные классы помощника EqualsBuilder и HashCodeBuilder от библиотека Commons Lang Apache. Пример:
public class Person {
private String name;
private int age;
// ...
@Override
public int hashCode() {
return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
// if deriving: appendSuper(super.hashCode()).
append(name).
append(age).
toHashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Person))
return false;
if (obj == this)
return true;
Person rhs = (Person) obj;
return new EqualsBuilder().
// if deriving: appendSuper(super.equals(obj)).
append(name, rhs.name).
append(age, rhs.age).
isEquals();
}
}
При использовании основанного на хеше Набор или Карта такой как [1 117] HashSet, LinkedHashSet, HashMap, Хеш-таблица , или WeakHashMap, удостоверяется, что хэш-код () ключевых объектов, которые Вы помещаете в набор, никогда не изменяется, в то время как объект находится в наборе. Пуленепробиваемый способ гарантировать это состоит в том, чтобы сделать Ваши ключи неизменными, , который обладает также другими преимуществами .
Можно было бы, чтобы класс Abstract выполнял вызов таким образом. В противном случае в C # нет способа потребовать, чтобы унаследованный класс реализовал метод определенным образом.
public abstract class AbstractClass
{
public void PerformThisFunction()
{
MustBeCalled();
AbstractMethod();
}
public void MustBeCalled()
{
//this must be called when AbstractMethod is invoked
}
//could also be public if desired
protected abstract void AbstractMethod();
}
public class ImplementClass : AbstractClass
{
protected override void AbstractMethod()
{
//when called, base.MustBeCalled() must be called.
//how can i enforce this?
}
}
При этом создается желаемый общедоступный метод в абстрактном классе, давая абстрактному классу информацию о том, как и в каком порядке вещи вызывается, при этом позволяя конкретному классу обеспечивать необходимую функциональность.
How about
public abstract class AbstractClass
{
public void AbstractMethod()
{
MustBeCalled();
InternalAbstractMethod();
}
protected abstract void InternalAbstractMethod();
public void MustBeCalled()
{
//this must be called when AbstractMethod is invoked
}
}
public class ImplementClass : AbstractClass
{
protected override void InternalAbstractMethod()
{
//when called, base.MustBeCalled() must be called.
//how can i enforce this?
}
}
Одна вещь, которую игнорируют предыдущие решения, это то, что ImplementClass может переопределять MethodToBeCalled и не вызывать MustBeCalled -
public abstract class AbstractClass
{
public abstract void AbstractMethod();
private void MustBeCalled()
{
//will be invoked by MethodToBeCalled();
Console.WriteLine("AbstractClass.MustBeCalled");
}
public void MethodToBeCalled()
{
MustBeCalled();
AbstractMethod();
}
}
public class ImplementClass : AbstractClass
{
public override void AbstractMethod()
{
Console.WriteLine("ImplementClass.InternalAbstractMethod");
}
public new void MethodToBeCalled() {
AbstractMethod();
}
}
Если только C # позволяет запечатывать непереопределенные методы - например, ключевое слово final в Java!
Я могу придумать единственный способ преодолеть это - использовать делегирование, а не наследование, потому что классы могут быть определены как запечатанные. И я использую пространство имен и «внутренний» модификатор доступа, чтобы предотвратить предоставление новой реализации для реализации классов. Кроме того, метод для переопределения должен быть определен как защищенный, иначе пользователи могли бы вызвать его напрямую.
namespace Something
{
public sealed class OuterClass
{
private AbstractInnerClass inner;
public OuterClass(AbstractInnerClass inner)
{
this.inner = inner;
}
public void MethodToBeCalled()
{
MustBeCalled();
inner.CalledByOuter();
}
public void MustBeCalled()
{
//this must be called when AbstractMethod is invoked
System.Console.WriteLine("OuterClass.MustBeCalled");
}
}
public abstract class AbstractInnerClass
{
internal void CalledByOuter()
{
AbstractMethod();
}
protected abstract void AbstractMethod();
}
}
public class ImplementInnerClass : Something.AbstractInnerClass
{
protected override void AbstractMethod()
{
//when called, base.MustBeCalled() must be called.
//how can i enforce this?
System.Console.WriteLine("ImplementInnerClass.AbstractMethod");
}
public new void CalledByOuter()
{
System.Console.WriteLine("doesn't work");
}
}
Why can't you just call the method in the AbstractMethod() of Implement class?