Home > Mobile >  Why can't I access a private (nestmate) class member via generics?
Why can't I access a private (nestmate) class member via generics?

Time:11-11

I have something equivalent to this:

class Main {
    public static class Data {
        private void foo() {}
    }
    
    public <D extends Data> D process(D data) {
        data.foo();
        return data;
    }
}

The compiler complains:

The method foo() from the type Main.Data is not visible

I don't see why this shouldn't work. It compiles fine if I don't use generics in process:

    public Data process(Data data) {
        data.foo();
        return data;
    }

I want to use generics here so I can keep a fluent interface that returns the exact subclass of Data that is passed in, but I also want to keep Data's base members private to the outer class.

Why is this happening and what are my options?

CodePudding user response:

even though you dont use generics , this problem will be there. D is not a "Data" for compiler , it is some other type which extends "Data".

public class Main {

    public static class Data {
        private void foo() {}
    }
    
    public static class DataX extends Data
    {
        
    }  


    public DataX process(DataX data) {
        data.foo();  // will not be compiled because foo() has private access
        return data;
    }
}

as a solution you can directly cast it to a local variable of type Data , or you can do it in tricky way (same thing, suits if process method has some logic inside) like this.

public class Main {

    public static class Data {
        private void foo() {}
    }

    public <D extends Data> D process(D data) {
        internalProcess(data);
        return data;
    }
    
    private void internalProcess(Data data)
    {
        data.foo();
    }
}

CodePudding user response:

The compile error is because even subclasses do not have direct access to private members. Your generic method declares D as a subclass of Data, thus it does not have access to foo which is private. This is outlined in the docs.

The reason why you can do this without generics is because whilst foo is private, it is visible at the class level and thus Data and Main. If the Data class declaration was separate (file or package) then Main would not have access either.

That said, a simple solution is to assign to a local variable as per shmosel's comment:

public <D extends Data> D process(D data) {
    Data d = data;
    d.foo();
    return data;
}

We could also make this more concise by using casting:

((Data) data).foo();

In either case, this is type safe because D is a type of Data (as declared in the method signature) so we can treat it as such, and we don't break the requirement to keep the member private.

Alternatively, if we don't strictly require the members to be private, we could declare them protected or package private (no modifier).

  • Related