开发者

Restrict Constructor to Sibling use - Java

This might not be possible but I am trying to create a constructor that only classes that share a super class can access, almost a reverse logic of the protected modifier. I assume there is no modifier to accomplish this directly, but knowing what I am trying to accomplish, any suggestions?

public Account extends SomeEntity {

    //default public
    public Account() {

    }

    // I am wanting this constructor to be only available to sibling classes.
    // (those that share the same super class )
    <modifier> Account(Element accountElement) {

    }
}


public Accounts extends SomeEntity {

    private List<Account> accountList;

    //default public
    public Accounts() {

        Account newAcct = new Account(element);

        //looped loading up Generic list of Account
        this.accountList.add(newAcct);
    }

I am working with RESTful web services and building the Objects out of XML responses, the problem is if I GET a listing of accounts, to build that into a list of Ac开发者_Go百科count Objects I would have to query the web service for each individual account even though I already have the information, and that seems entirely inefficient.

BUT

I don't want to give a general user, of the API I'm building, to be able to instantiate an Account Object this way. (With an Element)


There is no language construct like this. Package (=default) access is the only Java mechanism in town, as of 1.6.

I'm sure you could do nasty things with the stack, but I wouldn't recommend them.


I'd take a look at the factory pattern. You can probably play games with the access modifiers of the factory method(s) to get something close to what you want. You might also be able to play with reflection inside the factory method to get something closer to what you want than what package access gets you.


Sorry but I still don't get the point of this design. If a method is added to a class, its implementation will probably use private data to this class only, and therefore no guarantee can be made to 'sibling' classes that this data is also available for them. In other words, if your wish was granted, how would you guarantee that constructor Account(Object arg0) implementation won't use private data to Account class? (and therefore invisible to Accounts class)

It seems to me like you desire your code to provide the same interface for a single account and a list of accounts - extending SomeEntity class. That can be accomplished more elegantly with a composite pattern.

http://en.wikipedia.org/wiki/Composite_pattern

if your intent however is to provide a custom constructor that only subclasses will use, why not declare the custom constructor in SomeEntity and making this class abstract?

also, remember you can do this:

public Account() {
  this(new arg0());
}

Account(Object arg0) {

}

Not sure if this helps, though.


There is a way to emulate the C++'s friend feature, and thus achieve the result you want.

Warning: This is a contrived technique that should be used only if you have no other solution!

Since no modifier does what you want in this case, the trick is to move the access restriction to another place, where modifiers apply. To do that, add a key parameter to the constructor. That key is of a class that can only be instantiated by the allowed "sibling" classes, i.e. by the subclasses of a given class.

The restriction is thus moved to the common superclass, where restraining the creation of the key is possible with the usual modifiers.

Here is an example:

public class CommonSuperClass {
    public static final class Key {
        private Key() {}
    }

    // This is the only way to create a key, and it's protected
    protected final Key createKey() {
        return new Key();
    }
}

public class Account {
    // The restricted constructor can even be public
    public Account(Key key) {
        // Everybody can try with null, but we're not that stupid
        // Of course any RuntimeException can be thrown instead
        if (key == null) throw new UnsupportedOperationException();
    }
}

public class AllowedSibling extends CommonSuperClass {
    public void foo() {
        // I'm allowed
        new Account(this.createKey());
    }
}

public class DeniedClass {
    public void foo() {
        // This doesn't compile
        new Account(new Key());

        // This will throw an exception
        new Account(null);
    }
}


This is a very strange requisite, and I think no access modifier can do what you want. Anyway, I recommend that you just make the constructors public and document them as "for internal use only".

If you really need to limit access you can use this wordy solution:

public class Base {
    protected interface Factory {
        Base getInstance(Element e);
    }

    private static Map<Class<?>, Factory> registry = new HashMap<Class<?>, Factory>();
    protected static void register(Class<?> c, Factory f) { registry.put(c, f); }
    protected static <T extends Base> T create(Class<T> c, Element e) {
        return (T) registry.get(c).getInstance(e);
    }
}

public class Derived1 extends Base {
    protected Derived1(Element e) { }
    private static class Derived1Factory implements Factory {
        public Derived1 getInstance(Element e) {
            return new Derived1(e);
        }
    }

    static {
        register(Derived1.class, new Derived1Factory());
    }
}

public class Derived2 extends Base {
    protected Derived2(Element e) { }
    private static class Derived2Factory implements Factory {
        public Derived2 getInstance(Element e) {
            return new Derived2(e);
        }
    }

    static {
        register(Derived2.class, new Derived2Factory());
    }

    public void method() {
        Element e = null;
        ...
        // Put some element in e
        ...
        // This is what you were trying to do
        Derived1 d1 = create(Derived1.class, e);
    }
}


public class SomeEntity
    protected void init(Element accountElement) {}

public class Account extends SomeEntity 

    public Account() 
        ....

    protected void init(Element accountElement)
        ....


public class Accounts extends SomeEntity

    Account newAcct = new Account();
    newAcct.init(element);


Here's what I would try (I have not tested this method):

<modifier> Account(Object arg) {
    if (!super.getClass().isAssignableFrom(this.getClass())) {
        throw new AssertionError("This constructor is only available to super classes.");
    } else {
        // Continue...
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜