views:

167

answers:

7

Ok, the question might not be crystal clear. Let me give some details:

Let's say I have an Shoe (CShoe) object factory class called CFactory. CFactory is a singleton class that creates and stores all instanciated shoes using a simple hashmap. It is then accessed through static methods to use the created objects.

Is there a way to force CShoe's constructor so that it can only called by the factory? (in other words, ensure that the creation of shoes can only be done by the shoes factory singleton class and not by other classes)

+1  A: 

You could give the CShoe constructor package access, and put both CShoe and CFactory in the same package.

Igor Krivokon
+1  A: 

Since you want to add each object to the map you could as well move this logic to the CShoe constructor - the object will add itsself.

sharptooth
+1  A: 
public class MyClass{

    // Package constructor
    MyClass(){

    }

}

By making the constructor with default access level only other classes in the same package will be able to invoke it. That's as close as you'll get.

Kris
Or explicitly specify the constructor as "protected"
Harry Lime
No, protected will not work, since he wants to call the CShoe constructor from CFactory.
Igor Krivokon
@raindog I beg to differ - try it out and see. Protected == Package Access Level. Also read here http://java.sun.com/docs/books/tutorial/java/javaOO/accesscontrol.html
Harry Lime
Protected constructors are also available to classes extending your class. If you tag your class as final then, yes, you'd be right.
Kris
@Harry Lime, protected is NOT the same as default access level (aka package private). Kris pointed out the difference. The link you posted is good - I think you should read it more carefully!
Jonik
Oops! LOL. Thanks @Jonik. Will teach me not to be so smug the next time!
Harry Lime
A: 

You could perhaps pass the factory object into the constructor?

public CShoe(CFactory factory)
{
    if (factory == null ||
          !factory.isValid()) // or whatever
    {
        throw new IllegalArgumentException();
    }
}
Harry Lime
+4  A: 

You could make Shoe an inner class of ShoeFactory:

public class ShoeFactory {

    public static class Shoe {
     private String name;

     private Shoe() {
     }

     private Shoe(String name) {
      this.name = name;
     }
    }

    public static Shoe createShoe(String shoeName) {
     return new Shoe(shoeName);
    }
}

I think this pretty much covers all the cases except .... reflection:

public class SmellyShoe {

    public static void main(String[] args) {
     try {
      java.lang.reflect.Constructor c = Shoe.class.getDeclaredConstructors()[0];
      c.setAccessible(true);
      Shoe smelly = (Shoe)c.newInstance(null);
      // grr
     } catch (InstantiationException e) {
      e.printStackTrace();
     } catch (IllegalAccessException e) {
      e.printStackTrace();
     } catch (IllegalArgumentException e) {
      e.printStackTrace();
     } catch (InvocationTargetException e) {
      e.printStackTrace();
     }
    }
}
bruno conde
You want the product to be a nested class of the factory??
Tom Hawtin - tackline
you can also make CShoe an inner class (not static class). by doing that, you will also prevent other people from being able to create an instance of CShoe without having an instance of CFactory.
Chii
Reflection is *always* able to sidestep access control modifiers with the setAccessible() method, if your example is able to run. You can stop this by running with a SecurityManager that disallows the "suppressAccessChecks" permission, see http://java.sun.com/docs/books/tutorial/essential/environment/security.html
Andrzej Doyle
A: 

Couldn't you just pass the caller instance as argument of a static member of shoe and in there do a check like "isInstanceOf" which calls the constructor if true?

felix
+1  A: 

Firstly, if you retain all created instances, that's known as a leak. I'll carry on assuming you mean a non-strong reference, bounded or some such cache, and also that Shoe is immutable.

Simply use a static method to return the factory.

public final class Shoe implements Footwear {
    private static final FootwearFactory<Shoe,Something> FACTORY =
        new FootwearFactory<Shoe,Something>() {
            ...
            public Shoe get(Something value) {
                value = new Something(value);
                ...
                return new Show(value);
            }
        };
    private static FootwearFactory<Shoe,Something> getFactory() {
        return FACTORY;
    }

    private final Something value;
    private Shoe(Something value) {
        this.value = value;
    }
    ...
}
Tom Hawtin - tackline
Hummm, so that's how a factory works. I was mistaken all this time... So, the factory should belong to the product ... nice !!!
bruno conde
"Belong" is a bit of a strong word. But constructors are part of their class, so it's not unreasonable that "virtual constructors" should also be there is simple cases.
Tom Hawtin - tackline