Ber*_*ard 11 java generics inheritance
我有一个问题,我实际上可以解决自己,但我仍然不明白为什么我的原始代码不起作用,或者是否有一个比我找到的更优雅的解决方案.我在这里展示了我的代码的简化版本.
考虑以下抽象超类X:
public abstract class X{
private int i;
public void m1(X x){
x.i = 1;
m2(x);
}
public abstract void m2(X x);
}
Run Code Online (Sandbox Code Playgroud)
当调用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;
}
}
Run Code Online (Sandbox Code Playgroud)
但是 - 我可以保证每个X子类实例的m1调用总是有一个相同类型的参数,例如当我有一个Y的实例时,方法m1的参数将始终是另一个实例Y.
由于这种保证,我想通过引入泛型来使演员不必要.这就是我希望我的子类看起来像这样的样子:
public class Y extends X<Y>{
private int j;
public void m2(Y y){
y.j = 0;
}
}
Run Code Online (Sandbox Code Playgroud)
超类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);
}
Run Code Online (Sandbox Code Playgroud)
但是 - 这不起作用,当我编译它时,我收到以下错误:
X.java:6: error: i has private access in X
Run Code Online (Sandbox Code Playgroud)
这通常是你试图访问另一个类的私人成员.显然,虽然我在声明中使用了"T extends X",但Java并不认识T总是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);
}
Run Code Online (Sandbox Code Playgroud)
至少我不再使用演员了 - 但为什么这个额外的作业是必要的呢?为什么原始代码不起作用?此外,我发现它很奇怪我必须使用X<?>,无法使用X<T>.
我相信我们可以将您的问题减少到:为什么以下示例无法编译?
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
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个很好的问题,它确实让我感到意外.但是我们实际上可以通过注意到这也不起作用从等式中删除泛型:
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 {
}
Run Code Online (Sandbox Code Playgroud)
换句话说,问题是你正在处理的是子类Foo,而不是Foo它自己.在这种情况下T,我们不知道哪个子类,但我们知道它是一个子类,或者Foo.
正如你已经想到的那样,解决方案(令人惊讶的是,至少对我来说)是向上的:
System.out.println(((Foo)baz).bar);
Run Code Online (Sandbox Code Playgroud)
或者对于通用案例:
public <T extends Foo> void printFoo(T baz) {
System.out.println(((Foo)baz).bar);
}
Run Code Online (Sandbox Code Playgroud)
演员是如此糟糕?并不是的.它肯定与避免使用中间变量的演员表一样好或更好.与任何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"
}
}
Run Code Online (Sandbox Code Playgroud)
在这方面,它更像是一个限定词,而不是一个演员.
| 归档时间: |
|
| 查看次数: |
8733 次 |
| 最近记录: |