Java – “with private access” errors and generics

I have a problem I can solve myself, but I still don't understand why my original code doesn't work, or if there is a more elegant solution than I found I provide a simplified version of my code here

Consider the following abstract superclass X:

public abstract class X{

  private int i;

  public void m1(X x){
    x.i = 1; 
    m2(x);
  }

  public abstract void m2(X x);

}

When we call M1, we manipulate the private field of X of the passed instance, and then we call M2 with that instance

I have several subclasses of X, they are all the same, and they also declare the private members they manipulate To achieve this goal, they always need to be an actor at the beginning of M2 This is one of them:

public class Y extends X{

  private int j;

  public void m2(X x){
     Y y = (Y) x;
     y.j = 0;
  }

}

But – I can guarantee that every M1 call of an instance of a subclass of X will always have the same type of parameters. For example, when I have an instance of Y, the parameter of method M1 will always be an instance of another y

Because of this guarantee, I want to make actors unnecessary by introducing generics That's what I want my subclass to look like:

public class Y extends X<Y>{

  private int j;

  public void m2(Y y){
     y.j = 0;
  }

}

How does superclass x look like now? My first attempt was:

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);

}

But – this doesn't work, and when I compile this, I get the following error:

X.java:6: error: i has private access in X

This is usually when you let you try to access private members of another class Obviously, Java doesn't know that t is always an instance of X, although I use "t extends X" in my declaration

I fixed x like this:

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);

}

At least I don't use actors anymore - but why is this extra task necessary? Why doesn't the original code work? In addition, I find it strange that I have to use x

Solution

I believe we can reduce your question: why can't the following example be compiled?

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

This is a good question. It really surprises me But we can actually remove generics from the equation, and note that this also doesn't work:

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

In other words, the problem is that you are dealing with a subclass of foo, not foo itself In the case of T, we don't know which subclass, but we know it is a subclass, or foo

As you've already thought, the solution (surprisingly, at least for me) is upcast:

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

Or for general cases:

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

Isn't the actor bad? It's not true It is absolutely as good or better as avoiding conversions using intermediate variables Like any upcast, I will assume that it will be deleted by the compiler It exists only as a compiler hint Of course, we don't have to worry about the safety of actors, because t's erasure is already foo

I can only assume that this restriction is necessary for clear access... Because subfoo can redeclare the bar itself, it may become blurred, which column is referenced, so the actor is necessary This is demonstrated in this complex example:

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

In this regard, it is used as a qualifier, not as an actor

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>