加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – “具有私有访问”错误与泛型

发布时间:2020-12-14 16:30:02 所属栏目:Java 来源:网络整理
导读:我有一个问题我可以解决自己,但我仍然不明白为什么我的原始代码不起作用,或者如果有一个比我发现的更优雅的解决方案.我在这里提供我的代码的简化版本. 考虑以下抽象超类X: public abstract class X{ private int i; public void m1(X x){ x.i = 1; m2(x); }
我有一个问题我可以解决自己,但我仍然不明白为什么我的原始代码不起作用,或者如果有一个比我发现的更优雅的解决方案.我在这里提供我的代码的简化版本.

考虑以下抽象超类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.

解决方法

我相信我们可以减少你的问题:为什么下面的例子无法编译?
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.

如你已经想到的那样,解决方案(令人惊讶的是,至少对我来说)是upcast:

System.out.println(((Foo)baz).bar);

或对于通用情况:

public <T extends Foo> void printFoo(T baz) {
    System.out.println(((Foo)baz).bar);
}

演员是不是很糟糕?不是真的.它绝对与避免使用中间变量的转换一样好或更好.和任何upcast一样,我会假设它将被编译器删除.它仅作为编译器的提示存在.我们当然不必担心演员的安全,因为T的擦除已经是Foo.

我只能假设这个限制是必需的,以便清楚访问…因为SubFoo可以重新声明条的本身,它可能会变得模糊,哪个栏被引用,所以演员是必要的.这在这个复杂的例子中得到证明:

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 "hellonworld"
    }
}

在这方面,它作为一个限定词,而不是作为一个演员.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读