Private class as return type from public method
Asked Answered
A

8

25

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?

Arbuthnot answered 16/12, 2013 at 12:29 Comment(0)
I
11

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

Inwardness answered 16/12, 2013 at 13:50 Comment(11)
One thing about what you say about making public methods in a private class. It does make a difference, if you have an object of that private class inside the parent class, you can't call a private method, right? So private/public methods in private classes are useful to define the usage in scopes with access to the class.Arbuthnot
Actually you can. Inner classes count as a member of the parent class for accessing private methods and variables (both from inner to outer and outer to inner).Inwardness
It's important actually. It lets you create inner classes with private methods and access them from the outer class without also having to expose them outside that class.Inwardness
Actually it is extremely common and useful to return private classes in properly designed Java code as long as the classes implement some interface. Java libraries do it all the time. Though they would probably return the interface type as opposed to the private class type.Roam
Exactly. The return type is the interface not the private class implementing it. returning a private class makes a lot of sense. Having a return type of a private class does not.Inwardness
It does make sens when you want to call the method from a scope that has access to the private class like inside Foo in this example.Asco
@JensSchauder Then your method should be private. Having a public method return/accept a private class is always bad design. The method should be private, the class should be public, or the method should return an interface and the class implement that interface. Nothing would stop your hypothetical in-scope code passing in an instance of the private class to a method which expected the interface.Inwardness
"Then your method should be private" why? If I need the result (as a public available interface or super class in the outer scope) making the method public is the right thing todo.Asco
@JensSchauder If you need the result then the result can't be private. Returning a private class is very little different from returning Object.Inwardness
@JensSchauder There is everything wrong with that. If it contains meaningful data you can't access it. If it's a handle then there is no compile time type safety checking on your uses of it. It's utterly useless and I'm slightly confused as to why you would even think otherwise? What is your useful use case for this?Inwardness
Let us continue this discussion in chat.Asco
D
3

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();
Deshabille answered 16/12, 2013 at 12:36 Comment(0)
R
1
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.

Randellrandene answered 16/12, 2013 at 12:32 Comment(8)
I downvoted. You hadn't answered the question at the time I did.Inwardness
You hadn't answered the question at the time I did. Are you serious?Randellrandene
@Adam Aroid good explanation. i do not know that why someone have voted down ??? i have voted up because i like your answer.Monarchy
Maybe I just read it too quick as I don't remember seeing the last line - which is the only bit which actually directly answered the question. Sorry about that, I did remove the down-vote as soon as I re-read it :)Inwardness
I edited my answer while you were reading the original I think.Randellrandene
It still doesn't really answer the question as to why its useful to return an object Bar that is invisible to the outside world though. Why might you ever want to do that and why might it be useful to do so?Inwardness
The question is about nested type visibility. Not about variables.Deshabille
I mentioned variables because it can make the reasons for type visibility easier to understand.Randellrandene
R
1

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();
   }
}
Reportage answered 16/12, 2013 at 12:33 Comment(3)
@JqueryLearner ok I updated to not confuse others. With namespace I just mean a space within a name is valid. How do you name it?Conferee
I have also worked on c# so aware of what namespace means in c#Kimberlite
@JqueryLearner I m sure that you know it. I just asked if you know how it is normally called in java. I thought that namespace would also be good for java.Conferee
M
1

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.

Mongrel answered 16/12, 2013 at 12:50 Comment(0)
L
0

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.

Lichenology answered 16/12, 2013 at 12:35 Comment(0)
D
0

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.

Deppy answered 16/12, 2013 at 12:58 Comment(0)
P
0

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 {
}
Profuse answered 17/8, 2018 at 11:6 Comment(1)
I guess in that case it would be more correct to have a third private method encapsulating that functionality. Basically what createPersonUI is already doing, and have createPersonUI just call this new private method. I think that because when, for example, generating documentation for this class, the return type of the method would be BasePanel, and the consumer of the documentation may not have access to knowing what BasePanel is or that it even extends Node, since it is private. But well, in all such scenarios if the tools are aware of this and show Node instead, it wouldn't be that bad.Arbuthnot

© 2022 - 2024 — McMap. All rights reserved.