Question

Why is this valid?

Foo.java

public class Foo {

    public Bar getBar() {
        return new Bar();
    }

    private class Bar {}

}

If Bar is private, how will users of this class use this method? Polymorphism can be used of course, but shouldn't this be invalid and the declaration should indicate this as returning an Object?

Was it helpful?

Solution

I've just been doing a bit of research on this and have not been able to find a definitive answer. It seems most likely that it is just an oversight on the part of the Java language designers and since it doesn't actually do any harm it has been left. It's no different really from putting a public method into a private class. Nothing stops you doing this, even though there is no way to actually access that public method.

Certainly NetBeans gives you the warning "Exporting non-public type through public API" when you try to do this. I expect most other environments will give a similar warning.

The returned object is entirely useless to anyone who tries to use it (unless they use reflection), pretty much all they can do is store it into an Object (or any other super class that they do have access to) and then pass that Object around.

You could potentially want to do this if the passed Object is being used as a "handle" that gets passed around but never operated on. In that case though it would still make much more sense to have the class public but make all the methods within it private to prevent them being acted on outside your class (or define a public interface to return and have the private class implement that).

So the answer seems to be:

It probably shouldn't be valid, but as it doesn't do any harm it has never been blocked.

There is a good answer here on a similar subject:

Exporting non-public type through public API

OTHER TIPS

Why is this valid?

Because client code in the call place might be expecting an Object(or not expecting anything at all), there is no problem with calling this method from anywhere:

Object o = new Foo().getBar();

It is valid because Bar is visible to the Foo class. Thus it compiles.

Of course another class can not see Bar and thus can not use the return value.

But another class can still just invoke the method without using the return value.

public class FooBar {

   public void invokeBar() {
        Foo foo = new Foo();
        foo.getBar();
   }
}

A public method returning a private class can be useful it you need to be able to call the method from any scope (e.g. to mutate an internal state of Foo), and for internal usage if you need any kind of result in addition of simply calling the method.

public class Foo {

    private String myString;

    public String getMyString() {
        return myString;
    }

}

This is valid as well. Why should inner classes behave differently?

Making Bar private only makes it invisible to the outside world just as making fields private.

One important caveat is that even if you are able to call getBar() on a Foo object you can't call methods of that reference (because of the visibility).

So the main thing is that you can do that but you should not do so.

The only situation I can imagine is when Foo is also an inner class and the outer class of Foo wants to use Bar.

Inner class

Inner classes represent a special type of relationship that is it can access all the members (data members and methods) of outer class including private. Nested classes can lead to more readable and maintainable code because it logically group classes in one place only.

It is one of the form of nested types. This kind of class declaration is known as inner class. If you declare the inner class as static, then it would be known as top-level nested class. Other forms of nested types available in Java are local class; class declared and defined within a block,ie, a method or a constructor or an initializer block. The fourth form of nested type is anonymous class; a class without any name whose object is used where the class is defined.

As far as your case is considered, i.e., inner class all the classes within a class can be declared with public, private and protected access specifiers. All the classes with in the enclosing class as well as enclosing class itself share a trust relationship. That means, all the private members of inner class as well as private members of enclosing class is shared among each other. However you cannot access the object of inner class without an object of enclosing class.

When you will try to create an object of inner class compiler would report a compile-time error. However following example access the private members of each other class, i.e., enclosing class access private members of inner class and inner class access private members of enclosing class :

class Bar {
    private static int x;

    public void getFoo() {
        System.out.println(new Foo().y);
    }

    private class Foo {
        private int y;
        public void getBar() {
            System.out.println(Bar.x);
        }
    }
}

public class Test{
    public static void main(String[] a) {
        Bar b = new Bar();
        //Bar.Foo f = new Bar.Foo(); This is completely illegal syntax.     
    }
}

Best example you could have for an inner class is the relationship of an Accounts class which is enclosing class and Transaction class which is inner class. One Accounts class can have more than one Transaction objects but Transaction object cannot exist without Accounts object.

Albeit, returning an object of private inner class is useless as it becomes invisible outside its class. As the above example of Accounts and Transaction class explains. Transaction cannot exists without Accounts object.

I have a perfectly valid use case for this, and I'm glad this is allowed.

Let's stay you have a class that creates UI pieces. It accepts somekind of domain object and creates a piece of UI:

public Node createPersonUI(Person person) {
    BasePanel panel = new BasePanel();
    // ... setup panel with values ...
    return panel;
}

BasePanel is a subclass of Node and is some internal class that the caller has no business with, as this class determines how things will look.

Now, I found myself needing to re-use part of this class when I needed to support a new object, PersonalProfile that contains much more information, but also contains the basic Person data:

public Node createPersonalProfileUI(PersonalProfile profile) {
    BasePanel panel = new BasePanel();
    // ... setup panel with values ...
    return panel;
}

However, that code was partially duplicated, so I did:

public Node createPersonalProfileUI(PersonalProfile profile) {
    BasePanel panel = (BasePanel)createPerson(profile.getPerson());
    // ... only setup missing values ...
    return panel;
}

The cast however is a bit ridiculous -- changing it to return BasePanel not only works, but doesn't expose any functionality of my private class. Instead it only exposes the methods from any public classes it inherits from... brilliant!

Full code:

public BasePanel createPersonUI(Person person) {
    BasePanel panel = new BasePanel();
    // ... setup panel with values ...
    return panel;
}

public BasePanel createPersonalProfileUI(PersonalProfile profile) {
    BasePanel panel = createPerson(profile.getPerson());
    // ... only setup missing values ...
    return panel;
}

private class BasePanel extends Node {
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top