Java 如何使用泛型限制对象创建?
我希望有一个类型层次结构,其中只有Java 如何使用泛型限制对象创建?,java,generics,Java,Generics,我希望有一个类型层次结构,其中只有Foo对象控制Bar对象的创建。例如: public abstract class Foo<F extends Foo<F>> { public abstract Bar<F> makeBar(); } public abstract class Bar<F extends Foo<F>> {} 如何限制Bar(仅使用类型系统,而不是运行时检查),使其必须由FooImpl的实例创建,而不能
Foo
对象控制Bar
对象的创建。例如:
public abstract class Foo<F extends Foo<F>> {
public abstract Bar<F> makeBar();
}
public abstract class Bar<F extends Foo<F>> {}
如何限制
Bar
(仅使用类型系统,而不是运行时检查),使其必须由FooImpl
的实例创建,而不能在任何其他地方创建?您可以创建makeBar
一个具体的方法,该方法调用一个受保护的抽象makeBar 0
,该方法进行实际创建。makeBar()可以根据需要获取结果并检查对象
public abstract class Foo<F extends Foo<F>> {
public Bar<F> makeBar() {
Bar<F> bar = makeBar0();
// check bar
return bar;
}
}
公共抽象类Foo{
公共酒吧makeBar(){
Bar=makeBar0();
//检查栏
返回杆;
}
}
如果您愿意,您可以在Foo的创建上添加一个检查,这可能会提高性能。i、 e.检查makeBar0()的返回类型 这不起作用(有限制:仅键入system,不进行运行时检查)。我们可以一般不允许子类化(final
class),也可以允许它。如果(公共)类不是final,则任何其他类都可以是子类
您可以尝试使用注释——就像发明注释一样,它列出了允许的类名,但这取决于对注释的处理
例如:
@AllowedSubclasses(classnames="com.example.FooBar; *.AnyFooBar; com.example.foobars.*")
public abstract class Bar<F extends Foo<F>> {}
@allowedsubclass(classnames=“com.example.FooBar;*.AnyFooBar;com.example.foobars.*))
公共抽象类Bar{}
然后,如果任何其他类将该注释类子类化,注释处理器将抛出一个错误
混合方法如何:在javaDoc和文档中用“handsoff”注释您的内部方法,任何违反都会导致运行时异常。调用该方法后,如果调用方是其中一个类的实例,则可以在该方法中验证允许使用此功能的类。这不是类型系统可以(或应该)执行的操作。如果需要访问限制,请使用Java的访问修饰符。但我认为这些也不能实现您非常具体和不寻常的要求 实际上,我认为它们根本无法实现:您希望一个类公开可见且非最终类,但允许调用其构造函数并仅将其扩展到特定类及其子类
对不起,不行。无论如何,重点是什么?与其依赖泛型等,更简单的方法是强制要求您需要一个
Foo
的实例来创建条
public Bar(Foo foo){...}
这样,没有人可以独立于Foo
创建一个条形图。这将耦合这两个类,并向用户指出这一事实。还有其他方法可以实现这一点。。。这只是一个例子请您详细说明一下,这听起来很有趣,但我不太明白…这依赖于makeBar()
中的运行时检查,OP希望避免这种情况。(我想你的意思是说,makeBar()
应该是一个具体的方法。)@Stephen C,谢谢。运行时检查不应该是一个大问题,因为它是一个不应该经常违反的规则。i、 e.任何开发人员在决定要做什么之前只会做几次我一直认为只有Foo
的子类才可以控制(受保护的内部类?)的“内置东西”,但我无法让它运行。即使没有,武器“反射/调用API”也可以摧毁任何屏蔽。如果它可以安全地“正常”使用,那就足够了。如果有人使用反射,它会在他的脸上爆炸,那么……我真的希望您必须向Foo
子类询问Bar
。在您的版本中,我所能做的就是运行时检查Foo
是否真的创建了Bar
?任何人只从Foo那里得到Bar而不从其他地方得到Bar会得到什么?这就是你需要做的answer@Java饮酒者:像FooImpl
这样的子类包含Bar
的某个子类BarImpl
,该子类应该从外部隐藏(只看到Bar
s)。关键是当FooImpl
返回一个Bar
实例时,它需要将其转换回原始的BarImpl
,只有在我们确定它确实是由FooImpl
首先创建的情况下,这才是安全的。如果您完全控制代码,那么运行时检查可能就足够了,但情况并非如此。好吧,这是公平的,但再说一次,为什么FooImpl需要将其转换回BarImpl?如果您有一个抽象类或接口,那么应该通过公共api访问它的功能。我想我想知道的是,FooImpl从BarImpl得到了什么,而它不能从Bar得到什么?例如@Java Drinker:我想有一种只处理Bar
s的“框架”。这意味着,框架的一个方法将接受一个BarImpl
(毕竟它是一个Bar
),但在处理/转换它之后,它的类型将丢失,我所拥有的将是一个Bar。关键是要尽可能地编写通用的代码。为了解决这个问题,我将Bar
s设置为尽可能哑的,并将逻辑放入Foo
s中。但我仍然需要以一种安全的方式检索该数据。“到底有什么意义?”在Haskell或Scala中键入类。
@AllowedSubclasses(classnames="com.example.FooBar; *.AnyFooBar; com.example.foobars.*")
public abstract class Bar<F extends Foo<F>> {}
public Bar(Foo foo){...}