简体   繁体   中英

How can a public method return a private type?

Why is it possible to return a private nested class from a public method in a public class? Shouldn't the compiler complain about the return type's visibility being less than the method?

public final class Outer {
    private static final class Configurator {
        private Configurator() {
        }
    }

    public static Configurator configure() {
        return new Configurator();
    }
}

You can call such a method from outside the class, but only if you are happy to throw away the result.

public class TestClass {
  public static void main(String[] args) throws Exception {
    Outer.configure(); // this is valid
  }
}

or if you are happy to refer to the result as an Object :

public class TestClass {
  public static void main(String[] args) throws Exception {
    Object o = Outer.configure(); // this is valid
  }
}

The compiler allows this because it doesn't break any rules of Java. Object is simply the only publicly available superclass of your private class.

I doubt there are many practical uses for this pattern. If you wish to return an opaque object, that's better done by passing back a public class with no public methods, since you can at least type-check it when passed back into classes that need to use it.

There is a strict requirement that interface methods must be public . So when the method returning a non- public type fulfills an interface contract it must be public :

class Foo implements Supplier<NonPublicType> {
  public NonPublicType get() { // must be public !
    …
  }
}

Further, it is still possible to call this method from outside the package if the declaring class and the method are public . But the result has to be assigned to an accessible super type of the non- public class if it's going to be used. Eg if NonPublicType from the example above implements CharSequence you could say CharSequence cs=foo.get(); outside the package (if we change Foo to public ).

Note that the method in question might override a super class method which returns a public type and return a more specific non- public type (aka Covariant return type ). That method might be called from classes within the same package making use of the more specific type.

A nested class is a member of its enclosing class, so just like in singleton design pattern, when you call the public method to get the private static instance only through that method, it is also possible to call a private static class as a member or one of its methods. The reason you nested it is probably one of the following:

  • Logically grouping classes that are only used in one place
  • To increases encapsulation
  • To get more readable and maintainable code

For example if it were private, you can't say:

OuterClass.StaticNestedClass nestedObject =
 new OuterClass.StaticNestedClass();

So you need a public method to access it in this case.

It's entirely possible and makes sense, although a short practical example is rather difficult. The private class may be returned in place of any of its superclasses. Object has been mentioned here, but it doesn't have to be Object. This example is a little on the wacky side, but it works.

abstract class Super {
    abstract void zipper();
}

final class Outer {

    private static final class Configurator extends Super {
        private Configurator() {
        }

        @Override
        void zipper() {
            System.out.println("Zip!");
        }
        public void zoomer() {
            System.out.println("Zoom!");
        }

    }

    public static Configurator configure() {
        return new Configurator();
    }

}

public final class PublicPrivate {

    public static void main(final String[] args) {
        /* Outer.configure returns an instance of Configurator, 
            a subclass of Super */
        final Super = Outer.configure();
        /* Configurator.zoomer() is not available, 
            because Configurator is private */
        // o.zoomer(); /* Uncomment this line and the compile will fail */
        /* But Super.zipper() is available,
            in the form in which Configurator overrid it */
        o.zipper();
    }

}

I think that's because how you're going to deal with the object in the callee can be deferred until the last moment. For example, suppose

private static class X{

}

public static MyClass.X getX() {
    return new MyClass.X();
}

then from another class, I do something like

public void get() {
    System.out.println(MyClass.getX());
}

I don't need to know what type X is here because println just wants its toString(), which is inherited from Object.

Another example

private static class X implements Serializable{

}

public static MyClass.X getX() {
    return new MyClass.X();
}

In this case, I don't know X, but I know it's Serializable, so I can do something like

public void get() {
    Serializable x= MyClass.getX();
}

What I can't do, of course, is to use the type X from outside MyClass, but until I really need it, it's just another instance of Object

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM