"has private access" error with generics

Viewed 11294

I had a problem I could actually solve myself, but I still don't understand why my original code doesn't work, or if there is a more elegant solution than the one I found. I'm presenting 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 m1 is called, we manipulate a private field of X of the instance passed, and then we call m2 with that instance.

I have several subclasses of X, they are all alike in the sense that they also declare private members which they manipulate. In order to achieve that, they always need to make a cast at the beginning of m2. Here 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 call of m1 of an instance of a subclass of X will always have a parameter that is of the same type, e.g. when I have an instance of Y, the parameter of the method m1 will always be another instance of Y.

Because of that guarantee, I wanted to make the cast unnecessary, by introducing generics. This is how I want my subclasses to look like:

public class Y extends X<Y>{

  private int j;

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

}

How does the superclass X have to look like now? My first try was that:

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 - that doesn't work, when I compile this, I get the following error:

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

That's usually what you get you try to access the private members of another class. Obviously, Java doesn't recognise that T is always an instance of X as well, although I used "T extends X" in the 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'm not using casts any more - but why is this extra assignment necessary? And why didn't the original code work? Also, I found it strange I had to use X<?> and could not use X<T>.

1 Answers
Related