The Source for Java Technology Collaboration
User: Password:



Andreas Schaefer

Andreas Schaefer's Blog

All equals() are not born equal

Posted by schaefa on October 29, 2004 at 02:37 PM | Comments (24)

During a discussion with a colleague we started talking about the problems of equals() with inheritance. He mentioned Joshua Bloch's Effective Java book which covers this topic quit well but more or less said that this problem cannot be resolved. This was enough to get me hooked onto this problem and I came up with a solution quite fast. Afterwards I started to think about other solutions and their problems. Because all of them made sense I started to question myself if my solution was even necessary and until now I am still not sure if it does or if it is just a solution for theorists in the ivory tower. But even if it is just theory I think it is an interesting problem to talk about.

The equals() method defined in java.lang.Object has some restrictions to its implementation. Among other things it needs to symmetrical meaning that target of the method can switch places with the argument and it must yield the same result (if A is equal to B then B must be equal to A). Also it must be transient meaning that if A is equal to B and B equal to C then also A must be equal to C. There are two common ways to implement an equals() method:

  1. The method ensures that it only compares to instance of its own kind (class)
  2. The method ensures that it tests against an interface of base class and only tests if they are equal to that. An example is the AbstractList in java.util.
The first is a simple solution but breaks if someone tries to extend this class. The second solution cannot test parts of the extension of a class without breaking the rules for equals.

Before I start explaining my solution I want to come up with a use case to reason why equals() should work with inheritance. The use case is quite synthetic but it is the best model I could think of that is short and intuitive. In math you have the irrational numbers representing numbers like 1, ½, pi or e. An extension of it are the complex numbers that contain two irrational numbers one for the real part and for the imaginary part. For more information visit Dave's short course on Complex Numbers. In complex numbers you can also express pure irrational numbers by setting the imaginary part to 0. This means that e = e + 0i. For us this means that an irrational number should be equal to a complex number if the imaginary part is set to 0. The algebra behind the complex numbers does ensure that equals is symmetrical and transient. Now, shouldn't this be good enough for us software developers to achieve the same? The first solution mentioned above fails because it breaks with inheritance and the second solution will break mathematical because any complex number with the same real part as the irrational number will be equal because there is no test against the complex part.

To solve the equals() test with inheritance the equals() method must be written with inheritance in mind because only a sub class is able to determine equality correctly but in order to support symmetry the base class must delegate the test to the sub class if a sub class instance is given. An equals method of the irrational number would look like:

 1 public boolean equals(Object o) {
 2    if(o == null) return false;
 3    if(o == this) return true;
 4    if(o instanceof Irrational) {
 5       if(o.getClass().equals(Irrational.class)) {
 6          return ((Irrational)o).real == real;
 7       } else {
 8           return o.equals(this);
 9       }
10    }
11    return false;
12 }
In line 5 we check if the given object is of the same type and then we test is like normal. In line 8 the given argument is of a sub class so we delegate the test to this sub class with this instance as parameter.

In the Complex class we need to adjust the equals method a little bit:\

 1 public boolean equals(Object o) {
 2    if(o == null) return false;
 3    if(o == this) return true;
 4    if(o instanceof Complex) {
 5       if(o.getClass().equals(Complex)) {
 6          return ((Complex)o).real == real && ((Complex)o).imag == imag;
 7       } else {
 8           return o.equals(this);
 9       }
10    } else {
11       if(o.getClass().equals(Irrational.class)) {
12          return imag == 0 && ((Irrational)o).real == real;
13       }
14    }
15    return false;
16 }
The only difference to the Irrational's equals method is that we here check in line 11 if the given object is of type Irrational and then we allow the instances to be equal if the imaginary part is set to 0 and the real part is equal. So line 8 in both classes ensures that the equals() method is symmetrical and line 11 to 13 in the Complex class ensures that it is transient. Even thought this looks great so far it has three problems:
  • All sub class must overwrite and adjust the equals() method otherwise line 8 in the base class will create an endless loop
  • The equals() method in the sub class cannot call the equals() method in the base class otherwise it ends up in an endless loop, too
  • Line 11 in the Complex class cannot check against a sub class of Irrational in a different branch (meaning it is not a sub class of Complex, too)

The solution above works quite well even when it has some restrictions. Still, you might think that this is not useful in the real world but imagine a scenario where you have an ArrayList of Irrational type (generic types in JDK 1.5). This allows you to add Irrational numbers but also Complex numbers. If the equals() method would not support inheritance you would not be able to find an Irrational instance with a Complex instance that represents the same irrational number because the imaginary part is set to zero. One would also fail if one will search a Complex number with imaginary part set to zero with an Irrational instance. In a world of distributed computing sometimes we cannot just change all the code and so inheritance provides a way to introduce changes with localized side effects. This also means we must put some effort in the design of a class so that inheritance does not break especially on crucial parts like equality.

Happy coding – Andy


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Your idea is much more complicated than necessary. Try writing your equals method like this:

    public boolean equals(Object o) {
    if(o != null && this.getClass().equals(o.getClass()) {
    // Do actual equality checking here
    } else {
    return false;
    }

    Notice how, rather than explicitly using the exact class name, this.getClass() is used to determine the current class of this object. This way, you it does both - returns false if comparing two instance with different classes, AND allows equals methods in subclasses to call super.equals() and have it behave appropriately.

    fyi I have one of my professors in college, Dr Stevenson (http://www.cs.uwec.edu/stevenson.htm) to thank for originally pointing this out.

    Posted by: paulrivers on October 29, 2004 at 09:28 PM

  • I think you missed the boat here. Your suggestion will only be able to yield true for intances of the class. As in my exmaple when Complex is a sub class of the Irrational class you are not able to check if an Irrational number in an instance of Complex is equal to an instance of Irrational. Of course, your suggestion does follow the rules of equals() but treats a sub class as if it were a completely different class.
    -Andy

    Posted by: schaefa on October 29, 2004 at 11:13 PM

  • I'm surprised to read about an Complex class subclassing Irrational - this would imply that every

    Complex Number is also a Irrational one, which is shurely not true. Inheritance should be vice

    versa, because there's no gain in implementing the "right equals()" in the "wrong system". If (!)

    we want to subclass one from the other, then I think it would be Irrational subclassing Complex.

    Another way to think about the relation between complex and irrational numbers (which are

    the computer's restricted real numbers) is to look on a complex number as beeing composed from to

    irrational (in mathematics: real) numbers, as described above. This would lead to a Complex class

    aggregating Irrational with multiplicity 2, and the aggregation would be a composition ( if you

    don't implement the Irrational's as shared).
    A third way is to let Complex and Irrational to be not equals-connected at all,

    independent of their inner structure. For Example java.lang.Short and java.lang.Long don't

    recognize each other as beeing equal, which makes sense in the given context (i.e. representing

    primitive types).
    As you see, all scenarios don't lead to any kind of equals() problem, which in my eyes

    renders the example a little bit useless. In my opinion the problem with equals() is, that the

    contract you should implement is not controlled by your code allone: the symmetry constraint

    'a.equals(b) == b.equals(a)' (having nulls just in mind here) must be recognized from two ends,

    i.e the class of a and the class of b. If you don't code-control both, you are not able to

    guarantee this contract. Treating others friendly is a necessary condition for good

    neighborship, but unfortunately does not imply to be treated friendly yourself. In essence, the

    equals() contract forces us to do things which we can't do - so in my opinion this is some kind

    of design flaw.
    To make your class a good citizen in a friendly world where no class is more equal than

    others, you have to know them all (i.e. their implementation of equals()). Even if you don't

    subclass any preexisting class, you still don't know if some class'es objects recognizes your new

    objects as always-unequal or sometimes-equal. For example: take the JDK classes as a Class-System

    respecting the equals-contract (which is sadly not true, have a look at the java.sql.Date

    subclasses), and name it A. Now write a final class EverybodysDarling with equals() as follows:
    (1) Not equal to any instance of any class in A.
    This should be checked class by class like this:

    if (other.getClass() != java.lang.String) {
    return false;
    } else if { other.getClass() != java.util.ArrayList) {
    return false;
    } ...and so on, through the jdk classes

    (2) Equal to anything else.

    Create a new Class-System B through adding EverybodysDarling to System A. In the new System,

    the equals() contract is still realized.

    Now think you have to write a class MyClass to extend System B to a new System C. Without knowing

    EverybodysDarling, you could easily break Symmetry not recognizing an EverybodysDarling as beeing

    equal - you will break the contract even if you don't implement equals() at all. So you

    have to implement a subtle contract somebody else (i.e. EverybodysDarling) has layed out before.

    To have Symmetry in the System C, you have to implement MyClass.equals() along this lines:

    .....
    if ( other instanceof EverybodysDarling) {
    return true;
    }
    ....

    But EverybodysDarling is not equal to any instance of any jdk-class. To have transitivity in

    System C, no instance of MyClass is allowed to be equal to any instance of any jdk-class, too.

    As you see, subclassing anything from the jdk with "value-semantic" would be quite difficult.
    The example shows how bad citizens far away in the java universe could render your equals()-

    job impossible - without having anything to do with your class at hand or the class you want to

    subclass. The reason is, that "fulfilling the equals()-contract" is a property of a System

    and a priori not a property of a class. But not ruling the world does not mean not to try to

    behave nice yourself - so normally it's possible to write good code for your classes equals()-

    Method, if you just know enough of the context, i.e how the others implement equals(). This

    knowledge is only available to somebody who comes later than the others. To be shure that nobody

    who comes later plays too bad with your class, one measure is to make your class

    final. I think that this is one reason that most of the "newer" java classes with "value-

    semantic" like java.util.Locale are final. Another measure is to compare classes with

    "this.getClass() == other.getClass()" as shown above. But: both measures do not prevent from

    adding stupid code like EverybodysDarling later.
    Finally., what comes to (my) mind are Friedrich Dürrenmatt's much more important words from "Die

    Physiker" about constructing the atom bomb: "Was alle angeht, können nur alle lösen", i.e (in my

    bad english:) "what concerns everybody could only be solved by all people together". So we can't

    solve the problem on our own, but we can decide to be part of the problem or part of the

    solution.I think the latter one works nice for me.
    -Jan

    Posted by: bender on October 31, 2004 at 02:48 AM

  • I don't get it; why can't equals() work? I programmed it for entire hyrarchies and never had any problem.
    As long as your objects extending another object (Not Object) do a: if(other instanceof Foo) {
    return member1.equals(o.member1) && ... && super.equals(o));
    } return false;
    When will that fail?

    Posted by: zander on October 31, 2004 at 11:43 AM

  • Your primary idea seems to extend a Datatype not only with new fields but also prolong the equals contract - which surely is the right way to do it. But look at this example:


    public class NumberedDate extends Date {

    private int number;

    public NumberedDate(int millis, int number) {
    super(millis);
    this.number = number;
    }

    public boolean equals(Object o) {
    if(o instanceof NumberedDate) {
    NumberedDate other = (NumberedDate) o;
    return number == other.number && super.equals(other);
    } return false;
    }

    public static void main(String[] args) {
    NumberedDate cd = new NumberedDate(1000,5);
    Date d = new Date(1000);
    System.out.println(cd.equals(d));
    System.out.println(d.equals(cd));
    }
    }

    If I have not missed your point, this would be an implementation as you suggested. But running it gives us:

    jan$ java NumberedDate
    false
    true

    As you see, symmetry is violated. The reason is, that java.util.Date just uses other instanceof Date in its equals() method, and then compares the milliseconds. So even if java.util.Date is not final, you could not add some fields in a subclass and take these fields into account during an equals comparison - without breaking the symmetry. Any instance of any subclass of java.util.Date must equal to any other subclass of java.util.Date must only be compared by their milliseconds.
    To get even more confused, run this one:

    public class Timebandit {

    public static void main(String[] args) {
    java.util.Date d = new java.util.Date(0);
    java.sql.Timestamp t = new java.sql.Timestamp(0);
    t.setNanos(1);
    System.out.println(d.equals(t));
    System.out.println(t.equals(d));
    }
    }

    ..resulting in:

    jan$ java Timebandit
    true
    false

    As you see, even the jdk classes could not do equals() right - which is documented on the javadoc page for java.sql.Timestamp, along with some strange notes about 'implementation inheritance' versus 'type inheritance' - whatever this should be...
    To compile my last posting in one sentence, what I've meant there is: Even if you do your best in implemeting equals() (which you should), you can not protect the equals()-contract from beeing rendered impossible by others.
    -Jan

    Posted by: bender on October 31, 2004 at 01:21 PM


  • This blog was about how to write an equals() method within extended classes so that it still works with the requirements from the JDK. One of my conclusion is that the base class has to provide the means for the sub class to write a compliant equals() method by delegating the call to the sub class equals() method when a sub class is detected. Therefore extending java.util.Date must fail because it does not provide such means. Another restriction in my suggestion is that it only work for one branch in the inheritance tree meaning that two direct sub classes of a base class will still be treated as if they were complitely different.

    -Andy

    Posted by: schaefa on October 31, 2004 at 03:15 PM

  • I suspect that most programming doesn't need the type of equality found with mixed sets of Complex and Real numbers. Rather the simple approach of comparing the values of getClass() for both objects and only proceeding with the test if they match works for most. In particular this technique (unlike the instanceof approach) ensures symmetry and transitivity.

    To allow equals to work as mathematicians expect with number systems, an alternative approach might be to require a method which gives the simplest type of an object. Thus a complex value with an imaginary part of zero should report "Real", while one with a real part of zero would report "Imaginary" and only one with both parts non zero would report "Complex". We then proceed with comparison only if the two values report the same type.

    Posted by: mthornton on November 01, 2004 at 05:53 AM

  • What a horrible problem. I don't think it is really an Ivory tower one myself.

    I don't see a real solution for java that is backwards compatible. Perhaps one day sun will make a successor to java that fixes various weird problems like this one.

    What I think this comes down to is that it is best to use Comparator and avoid equals.

    However, there is still the issue of HashMap, HashSet, etc. I would kind of like to have a Hasher interface that could be given to these guys. This would play the role that Comparator plays for TreeMap.

    Posted by: regexguy on November 01, 2004 at 06:40 AM

  • In languages with multiple dispatch some of the avoidable problems disappear the most appropriate version of the equals function is selected

    what you've done is to manually simulate double dispatch by using java's polymorphic dispatch on the argument passed in - so this is good but its going to confuse a lot of people who are unaware that you are manually simulating something that is done in other languages and can think about it in the terms of the primitives those languages provide

    some more issues explored in:
    http://citeseer.ist.psu.edu/cache/papers/cs/3317/http:zSzzSzwww.cs.iastate.eduzSztech-reportszSzTR95-08.pdf/bruce95binary.pdf

    Posted by: asjf on November 01, 2004 at 07:34 AM

  • I can only emphasise bender's comments. What on earth are you doing, having Complex extend Irrational? Instead, write Complex as an immutable class. Irrational extends it but prevents you from creating an Irrational instance with a complex component. Implement a simple equals() method at the Complex level and make it final.

    The solution is simple, appropriate to the logical entities involved and not worth writing a blog over to inflate your ego.


    public class Complex {
    private double real_;
    private double imag_;
    public Complex(double real, double imag) {
    real_ = real;
    imag_ = imag;
    }
    public double getReal() { return real_; }
    public double getImaginary() { return imag_; }

    public final boolean equals(Object o) {
    if (o instanceof Complex) {
    Complex c = (Complex) o;
    return real_ == c.real_ && imag_ == c.imag_;
    }
    return false;
    }
    }

    public class Irrational extends Complex {
    public Irrational(double value) {
    super(value, 0);
    }
    }

    Posted by: oxbow_lakes on November 01, 2004 at 07:52 AM

  • The fact that complex extends irrational when it probably shouldn't is beside the point. It was only an example to illustrate the problem.
    Sometimes you do want to extend a class and change the equals method, but once you do you've introduced asymmetry. I don't think making equals final will solve the problem in general.

    Posted by: regexguy on November 01, 2004 at 08:30 AM

  • Ok, let me inflate my ego a little bit more. I am just asking 'Oxbow' why to the hell is there no Complex class in Java so far. You comment does only work if every developer has the chance to creates its own set of numbers and never has to use an existing class and then to extend this one. But just for the sake of the argument here we want to accept your proposal and I am going to use these classes in an application with an ArrayList<Complex>. All looks good until I am asked to add an implemenation of the Hamilton numbers. The only way to achieve that is to create a class Hamilton that extends java.lang.Object and I cannot use the application with the ArrayList<Complex>. What 'Oxbox' is requesting is that we are able to provide the most specialized class first which is not possible and that we create a new branch of classes when need a more specialized class but that is going to conflict heavily with generics.
    -Andy

    Posted by: schaefa on November 01, 2004 at 08:57 AM

  • i think "equals()" design is basically insufficient, but a handy pragmatic chice.
    Equality is an algebrical operation on type instances, another one if Comparability, (partial or total), another one could be monid/group opererations (add(), opposite(), isZero(), subtract() ), ring/field operations (group + multiply(), inverse(), isUnit() ) ...
    this must be approached more algebrically, and in which relations should apply among Types (Classes) and Operations (Algebras) ?
    (how many algebras for a type or a set of types) ?

    this take the problem toward "operation overloading" ...
    C# operation overloading implementation seems quite well implemented (implicit/explicit type cast/promotion), static member operations ...

    a deeper insight is provided by Haskell language: http://www.haskell.org/

    very cool 4 me ...

    generics can help ?

    public class SampleAlgebra {
    static boolean equals(T a, S b);
    }


    BTW:

    1) hashCode inheritance MUST be provided as well

    2) the equals contract imply that object propertis used for equality test MUST be immutable!!!

    ( consiter storing an object in a map and later changing fields ...)

    Posted by: hute37 on November 02, 2004 at 05:39 AM

  • Reposted


    I think equals() design is basically insufficient, but a handy pragmatic chice.

    Equality is an algebrical operation on type instances, another one if Comparability (partial or total),
    another one could be Monoid/Group opererations:

    add()
    subtract()
    isZero()
    opposite()


    Ring/Field algebric operation that adds:


    multiply()
    divide()
    isUnit()
    inverse()

    In particular these algebras could be definined with inheritance:

    Monoid Algebra {}

    Group Algebra extends Monoid {}

    Ring Algebra extends Group {}

    Field Algebra extends Ring {}

    A problem here is to relate Types (and set of Types) to Algebras (static import as a namespace import).

    (consider for instance Matrix/Vector/Scalar domains).


    This takes the problem toward "operation overloading" ...

    C# operation overloading implementation seems quite good
    implicit/explicit type cast/promotion, static member operations ...

    A deeper insight is provided by Haskell language:
    http://www.haskell.org/

    very cool 4 me ...


    Java generics can help ?

    public class SampleAlgebra {
    static boolean equals(T a, S b);
    }


    BTW:


    1) hashCode inheritance MUST be provided as well

    2) the equals contract imply that object propertis used for equality test MUST be immutable!!! ( consiter storing an object in a map and later changing fields ...)


    a final note: the author idea remind me a similar approach taken by Coplien in is very good Advanced C++ book

    Posted by: hute37 on November 02, 2004 at 06:03 AM

  • Andreas' basic idea : "Delegate equality testing to the subclass" is exactly the right idea. I'm impressed. It should be build into the Java core classes asap.

    The example of comparing Date and DateTime only goes to show how important the feature is. The same goes for Number types.

    The problem of equality across two branches in the inheritance tree might be addressed by taking advantage of transitivity of equals:
    objectOfSubclassA.equals(objectOfSubclassB) if and only if there exists an object p of a parent class of both A & B such that
    objectOfSubclassA.equals(p) && objectOfSubclassB.equals(p)
    Implementing this however would presumably require us to
    (1) walk the hierarchy to find the most recent common ancestor
    (2) construct an object p identical to objectOfSubclassA
    (3) Then compare p to objectOfSubclassB

    Posted by: xcarroll on November 02, 2004 at 07:13 AM

  • Well, delegating equality testing is very dangerous. If every one of your classes does this, you may find yourself in the situation where the two compared Objects happily call each others equals() method. That's recursion without an exit condition.
    The key problem here is that either way you will have to break encapsulation in equals(). At the core lies the minor snag that you have to know something about the other object in order to determine equality. But delegating the problem to the other class only works if the other class doesn't follow the same strategy.

    Posted by: bfandreas on November 02, 2004 at 08:46 AM

  • Notice the classloader-problem.

    Doing equals (the == version) on a Class instance makes me wonder if you should not do an .equals() on the Class.getName() instead since if one class is loaded by 2 classloaders the former will return false when the two instances might very well be equal.

    Naturally having one class loaded in 2 separate classloaders (in one JVM) is always tricky; but its also something you can solve with this idea.

    ps. thanks for the reply bender wrote; good explanation!

    Posted by: zander on November 02, 2004 at 09:03 AM

  • If you have the 'same' class loaded by two different classloaders, I think equals would have to return false. Comparing class names would not be sufficient for equality --- the code could still be different if each classloader had loaded from a different location.

    Posted by: mthornton on November 03, 2004 at 02:12 AM

  • Recursion
    I don't think recursion is a problem because delegation will only ever go from parentclass to subclass, which is a one way relationship
    Other posts
    Many of the comments seem to ignore the problem that andreas solves: how to recognise that a subclass object actually contains no subclass specific information and hence can be considered the same as an object of the parent class. This is a real world problem that comes up in several places, as illustrated already, and in other languages than Java. One that bugs me is the awkwardness of comparing dates, times and numbers stored in a relational database (eg a java.sql.Timestamp set to midnight tonight) with dates, times and numbers outside it (eg a java.util.Date set to midnight tonight). All the comments about comparing classes & class names fail to address this problem.
    Theoretically
    The reason I say that delegation to subclass is the right idea is: The subclass can be expected to know about the parent class. The parent class cannot be expected to know about the subclass. So the subclass is the least complex place to handle the problem.

    Posted by: xcarroll on November 03, 2004 at 03:23 AM

  • Ok, let me inflate my ego a little bit more. I am just asking 'Oxbow' why to the hell is there no Complex class in Java so far...

    You miss my point, which probably wasn't put very well. It is a failing of a huge number of developers to say to every little minor problem (like the one you posted) something along the lines of "Hey, I'll come up with an excrutiatingly complicated but generic solution", usually (as in this case) with several gotchas.

    There is no Complex class in Java for the same reason that there isn't a Fourier analysis package and a Galois theory API; there is no real market for it! You can obviously write your own API to deal with Complex numbers and write your own equals() method . Post it as open source if you like. But don't think that your solution to your little equals() problem isn't something that the likes of Josh Bloch haven't already thought of and discounted as being, generically, a bad one.

    far better just writing a solution appropriate to the problem at hand.

    Posted by: oxbow_lakes on November 03, 2004 at 03:36 AM

  • Oxbow: You are right to highlight the temptation to over genericise. But in this case, a generic solution is appropriate because it is a generic problem. It's a problem with equality testing that has to be manually worked around in many (not all) class hierarchies.
    It is not a little problem. Or by the same token, the casting problem solved by generics in Java 5.0 is a 'little' problem with an very complex solution that could be (and for the past 8 years has been) solved by each developer for their own special case. But the Wise Ones responsible for Java have gone for generics.
    There are not several gotchas. The only one I see with the proposal at the moment is: "the subclass developer can mess up". But that's not a gotcha for Andreas' proposal, it's a universal statement about developers on a learning curve.

    Posted by: xcarroll on November 03, 2004 at 04:16 AM

  • There are not several gotchas.

    Is that right? Is that really the only one you can see?


    As for schaefa's solution being truly generic - how come he has "Complex.class" written all over the equals () method he has developed?


    He has come up with a solution specific to his class hierarchy. The gotchas are to do with what happens when someone comes along and re-factors his hierarchy. What happens when someone inserts a class inbetween Complex and Irrational, what happens when someone subclasses Complex etc etc.

    A truly generic solution would be completely independent of class hierarchy and always come up with a sensible answer. Josh Bloch showed pretty conclusively how this can never be the case.

    And if you think that the fact a subclass developer can mess things up is a minor problem, think again; it's a MAJOR one. The poor subclass developer has NO IDEA that he HAS to overimplement equals() (and NOT call super.equals()).

    As for it being a little problem. It IS a little problem when you all you need to do is come up with a sensible way of defining equals on a few classes.

    Posted by: oxbow_lakes on November 04, 2004 at 01:27 AM

  • Well, it seems like there is no good solution in core java (see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5038615">http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5038615),
    and hence the suggested solution won't work. Notice the trick: Irrational instead of Double. That's because you cannot upgrade Double class. It is final! 'final' is like a copyright notice: all life ends there.

    I don't know what we can do about it. Maybe the humankind is not ready yet to deal with "parallel universes" - I mean to consider operations in different classes as belonging to different categories, and so equality in one class is not the same as equality in another class. There is no symmetry in 'equals' between objects from different categories. They are incomparable; you have to apply a functor to map the object to a specified category first.

    Have to go study Haskell probably to see how they deal with this.

    Posted by: vpatryshev on January 20, 2005 at 12:32 PM

  • Well, I did not talk about Double but Irrational so my solution does not end because of that. But I agree with many comments on top that this solution is not simple or straight forward to us. But again sometimes you are caught between a rock and a hard place and then you need to come up with tricky solutions.
    But in Java there are many little things that can go wrong. For example just have a method called in a constructor that is overwritten in a sub class that will use one of its own members. If for example this member is setup in the declaration (like myList = new ArrayList()) and you call a method on it, pang, you will get a NPE because the memeber will only be instanciated after the super class constructors returns.
    There are many easy way to screw up in Java and the only way to fix that is to talk about them.

    Posted by: schaefa on January 20, 2005 at 05:26 PM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds