我有一个问题,我实际上可以自己解决,但我仍然不明白为什么我的原始代码不起作用,或者是否有一个比我找到的更优雅的解决方案。我在这里展示了我的代码的简化版本。
考虑以下抽象超类X:
public abstract class X{
private int i;
public void m1(X x){
x.i = 1;
m2(x);
}
public abstract void m2(X x);
}
当调用m1时,我们操作传递的实例的X的私有字段,然后我们用该实例调用m2。
我有几个X的子类,它们都是相似的,因为它们也声明了他们操纵的私有成员。为了达到这个目的,他们总是需要在m2开始时进行演员表演。这是其中之一:
public class Y extends X{
private int j;
public void m2(X x){
Y y = (Y) x;
y.j = 0;
}
}
但是 - 我可以保证X的子类实例的m1的每次调用总是具有相同类型的参数,例如当我有一个Y的实例时,方法m1的参数将始终是Y的另一个实例。
由于这种保证,我想通过引入泛型来使演员不必要。这就是我希望子类看起来的样子:
public class Y extends X<Y>{
private int j;
public void m2(Y y){
y.j = 0;
}
}
现在超级X必须如何?我的第一次尝试是:
public abstract class X<T extends X<T>>{
private int i;
public void m1(T x){
x.i = 1;
m2(x);
}
public abstract void m2(T x);
}
但是 - 这不起作用,当我编译它时,我收到以下错误:
X.java:6: error: i has private access in X
这通常是你试图访问另一个类的私人成员。显然,Java并不认识T也总是X的一个实例,尽管我在声明中使用了“T extends X”。
我像这样固定X:
public abstract class X<T extends X<T>>{
private int i;
public void m1(T x){
X<?> y = x;
y.i = 1;
m2(x);
}
public abstract void m2(T x);
}
至少我不再使用演员了 - 但为什么这个额外的作业是必要的呢?为什么原始代码不起作用?此外,我发现奇怪的是我必须使用X<?>
而无法使用X<T>
。
答案 0 :(得分:7)
我相信我们可以将您的问题减少到:为什么以下示例无法编译?
public class Foo {
private final String bar = "bar";
public <T extends Foo> void printFoo(T baz) {
System.out.println(baz.bar); //bar is not visible
}
}
这是一个很好的问题,它确实让我感到意外。但是我们实际上可以通过注意这也不起作用从等式中删除泛型:
public class Foo {
private final String bar = "bar";
public void printFoo(SubFoo baz) {
System.out.println(baz.bar); //bar is not visible
}
}
class SubFoo extends Foo {
}
换句话说,问题是您正在处理Foo
的子类,而不是Foo
本身。在T
的情况下,我们不知道哪个子类,但我们知道它是一个子类,或Foo
。
正如你已经想到的那样,解决方案(令人惊讶的是,至少对我来说)是向上的:
System.out.println(((Foo)baz).bar);
或者通用案例:
public <T extends Foo> void printFoo(T baz) {
System.out.println(((Foo)baz).bar);
}
演员是如此糟糕?并不是的。它肯定与避免使用中间变量的演员表一样好或更好。与任何upcast一样,我认为它会被编译器删除。它仅作为编译器的提示存在。我们当然不必担心演员阵容的安全性,因为T
的删除已经Foo
。
我只能假设这个限制是必需的,以便明确访问...因为SubFoo
可以重新声明bar
本身,所以bar
被引用可能会变得含糊不清到,所以演员是必要的。在这个复杂的例子中证明了这一点:
public class Foo {
private final String bar = "hello";
static class SubFoo extends Foo {
private final String bar = "world";
}
public <T extends SubFoo> void printFoo(T baz) {
// System.out.println(baz.bar); // doesn't compile
System.out.println(((Foo)baz).bar); //hello
System.out.println(((SubFoo)baz).bar); //world
}
public static void main(String[] args) {
new Foo().printFoo(new SubFoo()); //prints "hello\nworld"
}
}
在这方面,它更像是一个限定词,而不是一个演员。