The Artima Developer Community
Sponsored Link

Weblogs Forum
Announcing Pojomatic 1.0

17 replies on 2 pages. Most recent reply: Apr 28, 2010 3:10 PM by Gregor Zeitlinger

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 17 replies on 2 pages [ 1 2 | » ]
Ian Robertson

Posts: 67
Nickname: ianr
Registered: Apr, 2007

Announcing Pojomatic 1.0 (View in Weblogs)
Posted: Apr 25, 2010 11:36 PM
Reply to this message Reply
Summary
Pojomatic provides configurable implementations of the equals(Object), hashCode() and toString() methods inherited from java.lang.Object.
Advertisement

Announcing Pojomatic 1.0

Version 1.0 of Pojomatic has been released. Pojomatic provides an easy way to implement the equals(Object), hashCode() and toString() methods inherited from java.lang.Object. The typical work needed for most classes is:

import org.pojomatic.Pojomatic;
import org.pojomatic.annotations.AutoProperty;

@AutoProperty // tells Pojomatic to use all non-static fields.
public class Pojo {
  // Fields, getters and setters...

  @Override public boolean equals(Object o) { return Pojomatic.equals(this, o); }
  @Override public int hashCode() { return Pojomatic.hashCode(this); }
  @Override public String toString() { return Pojomatic.toString(this); }
}

While this is adequate more most cases, there are numerous ways to customize how Pojomatic will build up the implementations of the equals, toString and hashCode methods. The @AutoProperty annotation can instruct Pojomatic to use fields or getters to accesses properties. Alternatively, one can annotate individual fields and/or accessor methods with the @Property annotation to include them explicitely, or to exclude certain properties if @AutoProperty is being used. For any property, a @PojomaticPolicy can be set to indicate which methods the property should be included in. By default, a property is used for each of the equals, hashCode and toString methods, but any combination is possible, subject to the restriction that if a property is used for hashCode, then it must be used for equals as well.

Proper implementation of equals for class hierarchies

As discussed in How to Write an Equality Method in Java, challenges arise in satisfying the contract for equals when class hierarchies come into play. The solution suggested in that article is to introduce an additional method, canEqual, which the implementation of equals will use to ensure that instances of a parent class do not accidentally declare themselves to be equal to an instance of a subclass which has redefined equals. If all the classes in a hierarchy use Pojomatic, this step is not necessary; Pojomatic keeps track of whether instances of two related classes can be equal to each other or not via the areCompatibleForEquals method. Two classes are compatible for equality if they each have the same set of properties designated for use with the equals method. If a subclass has a need to implement the equals method without using Pojomatic, it can be annotated with @OverridesEquals to indicate that it is not compatible for equality with it's parent class.

Comparing two Pojomated instances

A common use of the equals method is to facilitate the use of the assertEquals methods of JUnit or TestNG. When assertEquals fails, the exception method includes the toString representation of each instance. One pain point which no doubt will be familiar to many is that of trying to determine why two instances are not equal when they have a large number of properties. Often, the only option is to copy the failure message into an editor which allows comparing the toString representations to look for differences. Pojomatic helps address this by adding method, Pojomatic.diff which can reveal the differences between two instances of a Pojomated class. The PojomaticTestUtils library leverages this capability to provide an assertEqualsWithDiff method which will call out the differences between two instances in the failure messaged in the event that they are not equal.

Getting Pojomatic

Pojomatic is available on the central maven repository; if you use maven, it is just a dependency away:

    <dependency>
      <groupId>org.pojomatic</groupId>
      <artifactId>pojomatic</artifactId>
      <version>1.0</version>
    </dependency>
Others can download Pojomatic from the Sourceforge project page


John N Underwood

Posts: 1
Nickname: jnu
Registered: Mar, 2005

Re: Announcing Pojomatic 1.0 Posted: Apr 26, 2010 12:19 PM
Reply to this message Reply
This seems to have a subset in functionality of Project Lombok. Lombok also includes annotations for toString, equals and hashCode. But it also features annotations for close, synchronized, getters and setters. One last and very nice feature of Lombok: it integrates nicely with Eclipse.

http://www.ibm.com/developerworks/java/library/os-lombok/?ca=drs-
http://projectlombok.org/

- John

Ian Robertson

Posts: 67
Nickname: ianr
Registered: Apr, 2007

Re: Announcing Pojomatic 1.0 Posted: Apr 26, 2010 12:30 PM
Reply to this message Reply
> This seems to have a subset in functionality of Project
> Lombok. Lombok also includes annotations for toString,
> equals and hashCode. But it also features annotations for
> close, synchronized, getters and setters. One last and
> very nice feature of Lombok: it integrates nicely with
> Eclipse.

All good points. However, there are downsides to the extralinguistic approach that Lombok takes, most notably that IDE integration is non-trivial (they've done work for Eclipse, but not Idea or NetBeans). Additionally, it does not appear that Lombok's equals implementation handles class hierarchies as well, and there is not support for determining differences between two unequal instances.

Hopefully over time, Lombok will improve in these areas. Lombok is very nice solution, but it doesn't fit every project; our hope is that Pojomatic can help in places Lombok cannot.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Announcing Pojomatic 1.0 Posted: Apr 26, 2010 12:59 PM
Reply to this message Reply
This looks to be a very clever solution and I congratulate you on your achievement.

I must say, however, that I don't think you can really 'solve' the equals method problem in this way. Ultimately the problem with equals is that it's declared on the class that it evaluates. It's similar in my mind to the flawed measurement technique of attempting to measure the next length of board by the using the previous piece. Trying to scribe the next cut more accurately is attacking the problem from the wrong direction.

In concrete terms, when a subclass redefines equals, it's not always the case that you want it to always return false when compared against an instance of the parent type. Sometimes you want it to work the way it's defined on the parent and sometimes you don't. It depends on the context of the comparison and that's the key. In general, the equality relationship needs to be defined at the context level and not at the object/class level. For a lot stuff, a basic equals will work such as when there are no subclasses but it can never be a full solution and it's importance is therefore minimal.

Sean Landis

Posts: 129
Nickname: seanl
Registered: Mar, 2002

Re: Announcing Pojomatic 1.0 Posted: Apr 26, 2010 2:09 PM
Reply to this message Reply
As a user of Pojomatic, I can attest to its utility. Beyond the equals/hashcode capabilities (which are super), the toString() help is huge when debugging.

Ian Robertson

Posts: 67
Nickname: ianr
Registered: Apr, 2007

Re: Announcing Pojomatic 1.0 Posted: Apr 26, 2010 2:11 PM
Reply to this message Reply
> In concrete terms, when a subclass redefines equals, it's
> not always the case that you want it to always return
> false when compared against an instance of the parent
> type. Sometimes you want it to work the way it's defined
> on the parent and sometimes you don't. It depends on the
> context of the comparison and that's the key. In general,
> the equality relationship needs to be defined at the
> context level and not at the object/class level. For a
> lot stuff, a basic equals will work such as when there are
> no subclasses but it can never be a full solution and it's
> importance is therefore minimal.

You raise a really good point; the equals method, because it is part of the Object API, gets a lot of uses, not all of which are compatible. I personally find my most common use of it is with assertEquals; sets and maps come in a distant second, unless you count the equals methods on the types of very simple classes that tend to be used for keys in maps. That said, Object does define a contract for equals which is best not violated. In order to preserve symmetry, if a subclass is introducing additional constraints on the notion of equality, it is imperative that instances of a superclass not consider themselves equal to instances of the subclass.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Announcing Pojomatic 1.0 Posted: Apr 26, 2010 6:05 PM
Reply to this message Reply
> You raise a really good point; the equals method, because
> it is part of the Object API, gets a lot of uses, not all
> of which are compatible. I personally find my most common
> use of it is with assertEquals; sets and maps come in a
> distant second, unless you count the equals methods on the
> types of very simple classes that tend to be used for keys
> in maps. That said, Object does define a contract for
> equals which is best not violated. In order to preserve
> symmetry, if a subclass is introducing additional
> constraints on the notion of equality, it is imperative
> that instances of a superclass not consider themselves
> equal to instances of the subclass.

I'm surely at fault for not stating it clearly but I don't think you fully understood my meaning. Using the classic Point/ColorPoint, lets say that I want to determine whether two points are co-located. If my Points are colorless, this is simple, I ask the equals() method. If they are ColorPoints, then I can only do this in the case that the points are the same color. If one is a ColorPoint and the other is a Point, I can never use equals for this solution. So only in the case where I know that I have a homogeneous set of Points that are either all without color or that all have the same color, the equals method is basically useless. In other words, Color's equals method is basically useless if I want to use the powerful aspects of OO (as implemented in Java.)

The obvious solution is to define a new method aside from equals called areColocated (or whatever) that doesn't change. But if this is the solution, what's the point of defining a custom equals at all?

The thing is that there is already a solution to the same basic problem: Comparator. I use Comparators far more often than I implement Comparable. What I don't understand is why there is so much resistance to the idea of an Equalator interface. It resolves these issues in a much more useful way. In the above example, I could add all my points (regardless of type) to a single Equalator enabled HashSet defined to treat co-located points as equivalent and find out how many distinct points I have. In another part of the application I use the same approach to find how many different colors are used with my points. None of this requires any shenanigans around checking instanceof or class names.

Morgan Conrad

Posts: 307
Nickname: miata71
Registered: Mar, 2006

Re: Announcing Pojomatic 1.0 Posted: Apr 26, 2010 7:52 PM
Reply to this message Reply
+1 vote for "Equalator"

Bill Venners

Posts: 2244
Nickname: bv
Registered: Jan, 2002

Re: Announcing Pojomatic 1.0 Posted: Apr 26, 2010 7:55 PM
Reply to this message Reply
Hi James,

Did you ever see this article on equals?

http://www.artima.com/lejava/articles/equality.html

I think it addresses the Point/ColorPoint problem.

Bill

Raoul Duke

Posts: 127
Nickname: raoulduke
Registered: Apr, 2006

Re: Announcing Pojomatic 1.0 Posted: Apr 26, 2010 8:41 PM
Reply to this message Reply
> Hi James,
> Did you ever see this article on equals?
> http://www.artima.com/lejava/articles/equality.html
> I think it addresses the Point/ColorPoint problem.
> Bill

Bill, i went to read that to learn up, and saw that James commented extensively there.

Ian Robertson

Posts: 67
Nickname: ianr
Registered: Apr, 2007

Re: Announcing Pojomatic 1.0 Posted: Apr 26, 2010 11:13 PM
Reply to this message Reply
James, I think I see your point now. Notably, to achieve performance, you would probably want an EqualatorAndHasher, not just an Equalator; you'd also probably want a better name that what I just used :).

One option that can be achieved "with existing technology" is to create a wrapper class that defines equality and hashCode for your need, and then wrap and unwrap elements as you put them in and out of a collection. For example:
public class LocatedPoint {
  private final Point point;
  public LocatedPoint(point) { this.point = point; }
  public Point getPoint() { return point; }
  public int hashCode() { return point.getX() * 31 point.getY(); }
  public boolean equals(Object other) {
    if (other instanceof LocatedPoint) {
      otherLP = (LocatedPoint) other;
      return getPoint().getX() == otherLP.getPoint().getX()
        && getPoint().getY() == otherLP.getPoint().getY();
    }
    else {
      return false;
    }
  }
}

The down side to this is, of course, that clients now must go to extra work on every insert or retrieval from the collection. On the plus side, this allows use of the existing Collections classes without changing any existing contracts. As Kevin Bourrillion put it commenting on a proposal to add Equlator support to Google Collections, "Collections with nonstandard equivalence are spec violations that play badly with other kids, sometimes causing bugs in extremely nonobvious ways." (http://code.google.com/p/guava-libraries/issues/detail?id=188).

Another option is to create a wrapper class around, say, a Set which takes in it's constructor an EqualatorAndHasher, and then does the work under the covers. However, as Kevin points out, it would be unwise to have this wrapper class declared to implement Set.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Announcing Pojomatic 1.0 Posted: Apr 27, 2010 2:13 PM
Reply to this message Reply
> James, I think I see your point now. Notably, to achieve
> performance, you would probably want an
> EqualatorAndHasher, not just an Equalator; you'd also
> probably want a better name that what I just used :).

The Equalator would necessarily have a hash method. Without that, there's no real reason for it as the Comparator interface can be used for all purposes that don't require hashing (with a little extra work.)

> Another option is to create a wrapper class around, say, a
> Set which takes in it's constructor an EqualatorAndHasher,
> and then does the work under the covers. However, as
> Kevin points out, it would be unwise to have this wrapper
> class declared to implement Set.

There are already a number of Maps and Sets that exist in the JDK that allow for Comparators that can violate the equals contract. I can't think of a single issue that has occurred in any program where I've used one. I frequently use the String.CASE_INSENSITIVE_COMPARATOR as a parameter to a TreeMap.

I could only see this being an issue if you are passing in a map or set to an unsuspecting method or constructor. In that case, I would argue that there should be a clear definition of what the object or method receiving the map should expect and it's up to the caller to respect that.

I'd be curious to know some specific examples where this kind of thing has caused problems.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Announcing Pojomatic 1.0 Posted: Apr 27, 2010 3:28 PM
Reply to this message Reply
> The Equalator would necessarily have a hash method.
> Without that, there's no real reason for it as the
> e Comparator interface can be used for all purposes that
> don't require hashing (with a little extra work.)

On a side note, the stuff you've done here with Pojomatic could be trivially adapted to an Equalator methodology, right?

Roland Pibinger

Posts: 93
Nickname: rp123
Registered: Jan, 2006

Configurations and Annotations for equals? Posted: Apr 27, 2010 3:28 PM
Reply to this message Reply
How about that solution using only Standard Java:
 
public class MyObject {
  private String s;
  private Integer i;
  
  public boolean equals(Object other)  {
    boolean b = false;
    if (other != null) {    
      MyObject o = (MyObject) other;
      Object[] l = {s, i};
      Object[] r = {o.s, o.i};
      b = Arrays.deepEquals(l, r);
    }
    return b;
  }
 
  // ...
}

Just define an array of those fields that are correct for comparison (works for arrays and lists, too).
toString() and hashCode() can be implemented similarly.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Configurations and Annotations for equals? Posted: Apr 27, 2010 3:42 PM
Reply to this message Reply
> How about that solution using only Standard Java:

It's fine other than that it will throw exceptions if anything other than a MyObject is passed to it and violates the contract of equals if a subclass of MyObject with an overridden equals is passed in.

Flat View: This topic has 17 replies on 2 pages [ 1  2 | » ]
Topic: Syncing Motorola RAZR v3xx With Mac iSync 2.4 Previous Topic   Next Topic Topic: Heron 1.0 Alpha 3 : Compile-Time Reflection, the HeronEdit IDE, and more


Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us