Reputation: 2857
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?
Upvotes: 25
Views: 6113
Reputation: 8044
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 {
}
Upvotes: 0
Reputation: 41188
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
Upvotes: 11
Reputation: 4223
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();
Upvotes: 3
Reputation: 1007
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.
Upvotes: 0
Reputation: 30528
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
.
Upvotes: 1
Reputation: 2217
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.
Upvotes: 1
Reputation: 51353
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();
}
}
Upvotes: 1
Reputation: 6565
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.
Upvotes: 0