How to restrict object creation with generics?
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 Bar
s 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();
精彩评论