The recommendation TL; DR is: In case of doubt, be sure to have each class in your file .java
. More than anything if you work alone or do not have to consider a long life cycle for your code with others who have to work with your classes, it's usually not worth it to make so much drama with the "where" of the statement.
But there are good reasons because Java offers more possibilities to declare classes, and the alternatives have their benefits, if one has a good plan to take advantage of.
First you have to differentiate between inner classes ("inner classes" - classes defined within a body of another class but not static
) and nested static classes (" static nested classes
" - classes defined within the body of another class as static
).
Internal classes have a practical and obvious use, because they allow to adjust the level of encapsulation. These default classes can use the members of the objects in which they were instantiated, including private fields and methods. That allows a high degree of control over access to members of the external class.
In addition (and for that to work) you can not push an internal class outside of a method / constructor of the external class. This design decision clearly links the inner class with the outer class and prevents "abuse" of the class for other contexts that could have unexpected results.
A good example could be a class Validación
as inner class of a class Formulario
, which validates entry fields of the particular form.
Personally I regularly use indoor classes for "handler" or supervisors, that I want to make sure that they are created in the concrete context of an object of the outer class.
Nested classes to start with can be used as they are declared classes in their own files, but they have access to private members of the outer class. That again gives me more control over the visibility of methods and fields, exposing only what I want to expose in an API.
Furthermore nested classes can be declared private
, and so I can limit the generation of objects of this class to factory methods in the outer class:
Let's imagine that I have a Nido
class that can produce an amount of Dron
. Our drones are independent, complex workers and we do not want the user to mess with their construction, because we implement a great logic such as recycling and putting them to work:
public class Nido{
public static Runnable getDron(String mensaje){
return new Dron(mensaje);
}
...
private static class Dron implements Runnable{
private String mensaje;
private Dron(String mensaje){ this.mensaje=mensaje; }
@Override
public void run(){
// aqui implementemos nuestra idea genial
}
@Override
public String toString(){
return mensaje;
}
}
}
With this architecture we can implement a Nido
that manages completely without intervention permission or abuse of a user of our library the generation, interaction and life cycle of our objects type Dron
, and the user can not make a cast to Dron
. You are basically limited to calling the run
method and you can receive the message with toString()
.
Meanwhile the Dron
can use any method (static) deprived of Nido
, for example accessing a register of other Dron
to interact with the class Nido
or between pairs.
In summary :
While no way of declaring nested classes is necessary for good architecture, there are cases in which they can be used to create very fine control over access restrictions and abuse prevention by removing objects from the context so they were developed. When you need them because you run into limitations of the visibility modifiers, you will find out and develop your own ideas that can be done with that.
As a last warning, restrictions at this level are similar to tempting other programmers like "do not touch this jar, it is forbidden" for a 4-year-old child. Then hope that you will not miss someone who uses even reflection to do what you did not want to happen. But at least you can say it later: "If you lost your time, you got it." ;)