Ids and Ambiguous Method Parameters

March 14, 2010

You’ve probably run into a method with a signature like:

  void addToGroup(long userId, long groupId)

and a common question you might ask yourself is, “Oh, was it the user id first, or the group id first? I always forget. Stupid method.”

You’ve encoded the meaning of the parameters into their names, and hence you need to map these names to their position. In fact the compiler only sees this:

  void addToGroup(long, long)

and can’t help you out. You can add some Javadoc and your IDE can pop-up some information, but wouldn’t it be better if the compiler itself enforced that you could *never* get the arguments in the wrong order in the first place?

This is much better:

  void addToGroup(Id<User> userId, Id<Group> groupId)

Now you *have to* put the user id first and the group id second, otherwise the compiler will flag this statement as an error. Red squiggles.

What’s an Id?

 /**
* An {@link Entity} identifier.
*
* @param <E> the kind of {@code Entity} referenced by this kind of {@code Id}
*/
public final class Id<E extends Entity> {

 private final Long id;

 public Id(long id) {
   this.id = id;
 }

 /**
  * Creates an ID.
  */
 public static <T> Id<T> of(long i) {
   return new Id<T>(i);
 }

 /**
  * Gets the wrapped identifier.
  * @return the wrapped identifier
  */
 public long getId() {
   return id;
 }

 @Override
 @SuppressWarnings("unchecked")
 public boolean equals(Object that) {
   if (this == that) {
     return true;
   } else if (that == null) {
     return false;
   }
   if (this.getClass().equals(that.getClass())) {
     return this.getId().equals(((Id) that).getId());
   } else {
     return false;
   }
 }

 @Override
 public int hashCode() {
   return id.hashCode();
 }

 @Override
 public String toString() {
   return id.toString();
 }
}

And an Entity is a thing with an Id:

 /**
* An entity.
*/
public interface Entity {
 /**
  * Get this entity's id.
  * @return the entity's id
  */
 public Id<E extends Entity> getId();
}

We actually have an AbstractIdentifier that Id extends, but the above code captures the spirit of it.

In summary, with a typed identifier class you can help avoid ambiguous method parameters, making your code both easier to read and impossible to use incorrectly.

And happy PI day (3/14).