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){...}