3

I am trying to test the following situation:

  1. There is a main. (city.Main)
  2. There is a package. (package castle)
  3. Within the package, there is a public class (castle.Guard), and a package-private class (castle.Princess).
  4. What if, the public class is returning an instance of the private class?

Here is my code:

Main.java

package city;

import castle.Guard;

public class Main {

    public static void main(String[] args) {
        Princess princess = Guard.getPrincessStatic();
        // Error: Princess cannot be resolved to a type
        
        Guard.getPrincessStatic().sayHi();
        // Error: The type Princess is not visible
        
        Guard guard = new Guard();
        guard.getPrincess().sayHi();
        // Error: The type Princess is not visible
        
        guard.getPrincessMember().sayHi();
        // Error: The type Princess is not visible
    }

}

Guard.java

package castle;

public class Guard {

    public Princess getPrincess() {
        return new Princess();
    }

    public static Princess getPrincessStatic() {
        return new Princess();
    }
    
    private Princess m_princess = new Princess();
    
    public Princess getPrincessMember() {
        return m_princess;
    }
}

Princess.java

package castle;

class Princess {
    public void sayHi() { System.out.println("Hi world"); }
}

Notice all the 4 statements in main() are having errors.

I have done some research too. In fact i want to mimic this answer. But i don't why my codes throw errors.

Thanks for any explanations!


Edit:

I intend to make the castle-Princess package-private. I know that, by returning a package-private class out of its package, I should be prepared for errors. But why that answer works, while mine doesn't?

bruno
  • 2,213
  • 1
  • 19
  • 31
midnite
  • 5,157
  • 7
  • 38
  • 52
  • The errors are basically saying "private means *private*!" These links might help: [Java Language Spec: visibility](http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#d5e8032), http://stackoverflow.com/questions/5203205/why-cant-we-override-a-base-class-method-with-private-extended-class-method, [Visibility and Coupling](http://www.leepoint.net/JavaBasics/oop/oop-70-visibility.html) – paulsm4 Apr 13 '13 at 05:29

5 Answers5

5

Princess class is default scoped to castle package , so its invisible within city package . To circumvent that :

(We can do one of the following three approaches.)

1) Make Princess class public and use it .

package castle;

// added public modifier
public class Princess {
     public void sayHi() { System.out.println("Hi world"); }
}

2) Or, Define a public interface and let Princess implement it and use the interface reference instead of the class reference . I would prefer this .

castle \ IPrincess.java

// Interface definition
package castle;

public interface IPrincess {
    public void sayHi();
}

castle \ Princess.java

// Implement the interface in Princess class
package castle;

class Princess implements IPrincess {
    public void sayHi() { System.out.println("Hi world"); }
}

castle \ Guard.java

// Modify the Guard class
package castle;

public class Guard {
    public IPrincess getPrincess() {
        return new Princess();
    }
    public static IPrincess getPrincessStatic() {
        return new Princess();
    }

    // for here i use Princess instead of IPrincess. (@midnite)
    private Princess m_princess = new Princess();
    public IPrincess getPrincessMember() {
        return m_princess;
    }
}

city \ Main.java

// Modify the main class
  package city;

  import castle.Guard;
  import castle.IPrincess;

  public class Main {

  public static void main(String[] args) {

     IPrincess princess = Guard.getPrincessStatic();

     Guard.getPrincessStatic().sayHi();

     Guard guard = new Guard();
     guard.getPrincess().sayHi();

     guard.getPrincessMember().sayHi();
   }
}

3) Or, Put all the classes in same package .

midnite
  • 5,157
  • 7
  • 38
  • 52
AllTooSir
  • 48,828
  • 16
  • 130
  • 164
  • This is really a nice solution!! Please allow me to edit a little bit of your answer for better readability :-) – midnite Apr 13 '13 at 06:44
1

You are not going to be able to return a type that is privately scoped within a class. How can any external sources know what that type even IS if it's privately scope inside of another class?

This design won't even compile.

Edit:

The simple solution is to make it public and then you can use it externally outside of the package.

Matthew Cox
  • 13,566
  • 9
  • 54
  • 72
1

The answer you are trying to mimic works because all the classes are inside the same package.

If you put UntrustworthyFriend and Friend in different packages (as your scenario), the same problems will arise.

Princess can only be visible outsite it's package if public:

Princess.java

package castle;

public class Princess {
    public void sayHi() { System.out.println("Hi world"); }
}

Also, make sure to import it in Main.

acdcjunior
  • 132,397
  • 37
  • 331
  • 304
  • You are right! If i put `UntrustworthyFriend.java` and `Friend.java` in different packages, the line `friendWithSecret.getMySecret().reveal()` gives Error: `The type Secret is not visible`. So, in my point of view, what @Andy want to illustrate is just returning a private member variable, instead of a private class, that is not really special IMO (all our getter methods do so). – midnite Apr 13 '13 at 05:53
  • @midnite: @Andy says _"However, methods can still be invoked on `Secret` from within `UntrustworthyFriend`."_ Still, this only holds true if `Secret` is visible from `UntrustworthyFriend`. And it will only be so if `Secret` is either `public` or in the same package as `UntrustworthyFriend`. – acdcjunior Apr 13 '13 at 06:12
  • @ acdc junior: Yup. In `UntrustworthyFriend.main()`, i can even do `System.out.println( (new Secret("blah")).reveal() );`. i don't really need a `Friend` at all :-/ – midnite Apr 13 '13 at 06:19
  • Indeed. Who needs a friend when you got stackoverflow? :) – acdcjunior Apr 13 '13 at 06:56
0

The problem lies in a very small error in the definition of your Princess class:

package castle;

class Princess {
    public void sayHi() { System.out.println("Hi world"); }
}

All looks fine at first, but you're missing a public modifier before the class name:

package castle;

//here!
public class Princess {
    public void sayHi() { System.out.println("Hi world"); }
}

Without the public modifier, the princess class cannot be accessed outside of the file in which it is declared.

ApproachingDarknessFish
  • 14,133
  • 7
  • 40
  • 79
0

Your example is different because your classes are in different packages and Guard and Princess are not visible from Main.

But public method can return an instance of a private class. JDK is full of examples, here is one from java.util.Arrays

public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}
...
private static class ArrayList<E> extends AbstractList<E>  {
...

it is what you ask. The trick is that the method's return type is a public interface which the private class implements. You could do something similar.

Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
  • Thanks @ED. This is interesting! i guess this should be the trick implemented by @ noob earlier? – midnite Apr 13 '13 at 06:47