15

I'm looking for different ways to ensure that each instance of a given class is a uniquely identifiable instance.

For example, I have a Name class with the field name. Once I have a Name object with name initialised to John Smith I don't want to be able to instantiate a different Name object also with the name as John Smith, or if instantiation does take place I want a reference to the orginal object to be passed back rather than a new object.

I'm aware that one way of doing this is to have a static factory that holds a Map of all the current Name objects and the factory checks that an object with John Smith as the name doesn't already exist before passing back a reference to a Name object.

Another way I could think of off the top of my head is having a static Map in the Name class and when the constructor is called throwing an exception if the value passed in for name is already in use in another object, however I'm aware throwing exceptions in a constructor is generally a bad idea.

Are there other ways of achieving this?

13
  • 1
    You want a singleton
    – Marc B
    Commented Oct 22, 2012 at 17:45
  • 5
    you should better go with the first one: - static factory
    – Rohit Jain
    Commented Oct 22, 2012 at 17:46
  • 16
    @MarcB op doesn't want singleton. he might have many instances of the same class, but these instances must be identifiable.
    – BlueBullet
    Commented Oct 22, 2012 at 17:46
  • 1
    @MarcB I'm aware of the singleton pattern but I thought that ensures only one instance of a class is possible? I want multiple instances, different values. Sorry if the question isn't making that clear. edit: Only saw first comment before posting.
    – Peanut
    Commented Oct 22, 2012 at 17:48
  • 2
    I'm aware that one way of doing this is to have a static factory that holds a Map... So why don't you want to do it this way? Commented Oct 22, 2012 at 17:55

5 Answers 5

13

Actually you have already answered your question. Your first way should be more effective here. Using static factory is always preferable than constructor wherever you think you can. So, you can avoid using Constructor in this case, else you would have throw some exception if an instance already exists with the given name.

So, you can create a static factory method: - getInstanceWithName(name) which will get the already available instance with that name, and if it does not exist, it will create a new instance, and make your constructor private, as it should mostly be done when dealing with static factories.

Also, for that you need to maintain a static List or Map of all the unique instances created, in your Factory class.

EDIT: -

You should certainly go through - Effective Java - Item#1 : Consider Static factories over Constructors. You can't get better explanation than that book.

7
  • 1
    That's something which the OP already has thought of.
    – BGurung
    Commented Oct 22, 2012 at 17:57
  • +1 for the link, which seems to clearly address some of the OP's concerns. Commented Oct 22, 2012 at 18:15
  • @RobertHarvey. Yeah that book is the best source for most of the topics like these. And that's the very first item in fact. :)
    – Rohit Jain
    Commented Oct 22, 2012 at 18:16
  • +1 for Effective Java, I do have the book, sitting in my bag now actually :) However I was just curious as to other ways of achieving the uniqueness apart from the static factory/static method and the exception in the constructor. If there aren't any then I'll accept this.
    – Peanut
    Commented Oct 22, 2012 at 18:37
  • @Peanut. Sure. That is the best resource to dig in to get more information.
    – Rohit Jain
    Commented Oct 22, 2012 at 18:48
6

Mentions of Effective Java seems to add a lot of credibility so this answer draws on:

  • Effective Java Item 8: Obey the general contract when overriding equals
  • Effective Java Item 9: Always override the hashCode when you override equals
  • Effective Java Item 15: Minimise mutability

I would take a step back and question why you care if there are more than one instance of this name object.

I rarely need to do this kind of object pooling. It is my guess that the OP is doing this so they can simply compare their Name objects with ==. Or use the Name objects inside a HashMap or similar as the key.

If so this is something that can be solved through proper implementation of equals().

Like so:

public final class Name {
  private final String name;

  public Name(String name) {
    if (name == null) {
      name = ""; //or exception if you like
    }
    this.name = name;
  }

  public String getName() {
    return name;
  }

  @Override
  public boolean equals(Object o) {
    if (!(o instanceof Name)) {
      return false;
    }
    Name other = (Name) o;
    return other.name.equals(name);
  }

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

Once done the following is true:

Name a = new Name("weston");
Name b = new Name("weston");
assert(a.equals(b)); //same but:
assert(a!=b); //not the same instance
//test out the Name instances in a hashmap:
HashMap<Name,Object> map = new HashMap<Name,Object>();
Object detailsIn = new Object();
map.put(a,detailsIn);
Object detailsOut = map.get(b);
assert(detailsIn==detailsOut); //the map returned the same details object
//even though we put with `a` and got with `b` thanks to our correct equals implementation

I'm guessing your goal, but this way you can use the Name class in hash maps etc, and they do not have to be the exact same instance.

6
  • Example, please. Commented Oct 22, 2012 at 18:09
  • @RobertHarvey example of proper implementation?
    – weston
    Commented Oct 22, 2012 at 18:11
  • Looks like you provided one. But I'm not clear how this is any different from merely checking the Name property for equality in a static factory method. Commented Oct 22, 2012 at 18:13
  • 1
    @RobertHarvey I've tried to explain more, I am offering a complete alternative that does not require static factory at all and I am challenging the OPs starting point that they do not want to have more than once instance per name.
    – weston
    Commented Oct 22, 2012 at 18:40
  • A valid reason to require unique objects is if you want a synchronized block to use the object as a monitor. Two objects which .equals() each other would not work. Commented Dec 15, 2016 at 1:50
3
  • Make Name an interface
  • Create an interface NameFactory with a method Name getByName(String)
  • Create an implementation of NameFactory with a Map<String,WeakReference<Name>> inside it
  • synchronize on the map by name inside the getByName method before making new instances of Name
  • Optionally, use a static private implementation of the Name interface inside your implementation of the NameFactory

This approach would let you ensure that:

  • Only a single instance of Name exists at any time,
  • There is no "Lingerer" memory leak in your class, when Names stick around longer than they need to,
  • The design remains testable with mocked objects, because you use interfaces.
5
  • You don't have memory leaks with a factory method either, if you throw when the identical name exists instead of returning an object, or if you return a null object. Commented Oct 22, 2012 at 18:26
  • @RobertHarvey I mean the leaks that are not really leaks, but legitimate objects (the so-called "lingerers"). A simple static map would prevent its value objects from being released, while a map of weak references would hold them in memory only for as long as other live references are in existence. Commented Oct 22, 2012 at 18:34
  • Ah, I see what you mean. This might be one of the rare instances where a destructor would be handy. Commented Oct 22, 2012 at 19:30
  • 1
    Too complex. It can be done like @weston answer, override equals and have the factory keep a collection of names. Commented Oct 23, 2012 at 1:59
  • @user1598390 One should make thing as simple as possible, but no simpler. weston's "solution" does not solve the OP's problem (there is no map), and a collection of names creates a lingerer memory leak (yes, there are memory leaks in Java). Commented Oct 23, 2012 at 2:06
1

you should make the constructors private and create methods like getNameInstance(String), if a object with the same name already exists (based on a static class' hastable for instance), you return that reference, else you create a new object using your private constructor and add it to the hashtable

2
  • You've repeated what I said in the 3rd paragraph of the question. I was after alternative ways.
    – Peanut
    Commented Oct 25, 2012 at 0:53
  • I'm sorry, I think we answered almost at the same time because I checked for the answers before post mine. Actually, I tried to answer it on StackOverflown and than it was migrated.
    – HericDenis
    Commented Oct 25, 2012 at 9:39
1

Try following. You have to keep track of each object you create. For this purpose I'm using List. And made class constructor private, so that pre-check can be applied before creating an instance

class UniqueName
    {
        private string Name;
        public int ID;
        private static int Count=0;
        static List<UniqueName> lt=new List<UniqueName>();

        private UniqueName(string Name)
        {
            this.Name = Name;
            ID = ++Count;
        }

        public static UniqueName GetUniqueueInstance(string Name)
        {
            foreach (UniqueName un in lt)
            {
                if ( string.Compare( un.Name,Name,StringComparison.InvariantCultureIgnoreCase)==0)
                    return un;
            }

            UniqueName temp=new UniqueName(Name);
            lt.Add(temp);
            return temp;
        }
    }
2
  • This is not Java? Have you written a pseudo-code? Or, in any other language? Please specify that explicitly.
    – Rohit Jain
    Commented Oct 23, 2012 at 13:01
  • Looks like C#. Akshay, you should learn other collections other than List. This would be a good fit for Dictionary<string,UniqueName>.
    – weston
    Commented Oct 23, 2012 at 22:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.