开发者

How to restrict object creation with generics?

开发者 https://www.devze.com 2023-03-01 13:08 出处:网络
I want to have a type hierarchy where only Foo objects are in control of the creation of Bar objects. E.g:

I want to have a type hierarchy where only Foo objects are in control of the creation of Bar objects. E.g:

public abstract class Foo<F extends Foo<F>> {
    public abstract  Bar<F> makeBar();
}

public abstract class Bar<F extends Foo<F>> {}

Now a subclass of Foo could implement a subclass of Bar and give it back:

publi开发者_StackOverflowc class FooImpl extends Foo<FooImpl> {

    private static class BarImpl extends Bar<FooImpl> {}

    @Override
    public Bar<FooImpl> makeBar() { return new BarImpl(); }
}

However that does still allow the creation of Bars elsewhere:

public class FakeBar extends Bar<FooImpl> {}

How can I restrict Bar<FooImpl> (using only the type system, not runtime checks) in a way that it must be created by an instance of FooImpl, and cannot be created in any other place?


This isn't something the type system can (or should) do. You want access restrictions, use Java's access modifiers. But I don't think those can implement your very specific and unusual requirements either.

Actually, I don't think they can be implemented at all: you want a class to be publically visible and non-final, yet allow the ability to call its constructors and to extend it only to a specific class and its subclasses?

Sorry, no can do. What would be the point anyway?


That doesn't work (with the limitation: type system only, no runtime checks). We can either disallow subclassing in general (final class) or allow it. If a (public) class is not final, any other class may subclass.

You could try playing with annotations - like inventing an annotation, that lists allowed classname, but this depends on processing the annotations.

Example:

@AllowedSubclasses(classnames="com.example.FooBar; *.AnyFooBar; com.example.foobars.*")
public abstract class Bar<F extends Foo<F>> {}

The annotation processor then would throw an error, if any other class subclasses this annotated class.


What about a mixed approach: annotate your internal methods with "HANDS OFF" in the javaDoc and document, that any violation will result in runtime exceptions. After the method has been called, you can verify within the method, if the caller is an instance of one of the classes, that are allowed to use this feature.


Well instead of relying on generics etc, a simpler way to accomplish this is to enforce that you need an instance of Foo to create a Bar

public Bar(Foo foo){...}

This way, no one can create a bar independently of Foo. This couples the 2 classes, and indicates to users of this fact as well. There are other ways to accomplish this as well... this is just one example


You can make makeBar a concrete method which calls a protected abstract makeBar0 which does the actual creation. makeBar() can take the result and check it the object any way you wish.

public abstract class Foo<F extends Foo<F>> {
    public Bar<F> makeBar() {
        Bar<F> bar = makeBar0();
        // check bar
        return bar;
    }
}

If you prefer you could add a check on the creation of Foo which may be more performance. i.e. check the return type of makeBar0();

0

精彩评论

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