¿Usar get y set o declarar la variable publica?

publicado por: Anonymous

Muchas veces las variables de una clase en java se declaran como privadas para tener encapsulamiento.

Otras de las cuestiones para declarar una variable como privada es para que sólo pueda ser accesible desde la misma clase, pero finalmente en la mayoría de los casos se termina haciendo métodos set y get para modifcar la variable o para obtenerla fuera de la clase.

Entonces lo que me pregunto es si hay algún buen motivo para declarar una variable privada y ponerle set y get en vez de declararla publica y evitarse un set y un get.

solución

Permitir el acceso libre a los atributos de la clase no es un diseño condenado, solo es diferente. Usualmente se evita esto a modo de que la clase maneje al interno cualquier lógica de negocio que pueda asociar a los campos. Pör poner un ejemplo:

public class Foo {
    public List<String> listaNombres;
    public Foo() {
        listaNombres = new ArrayList<>(Arrays.asList("Friky", "Luiggi"));
    }
}

public class Otro {
    public List<String> listaNombresOtro;
    public void obtieneDeFoo(Foo foo) {
        listaNombresOtro = foo.listaNombres;
    }

    public static void main(String[] args) {
        Foo foo = new Foo();
        System.out.println(foo.listaNombres);
        Otro otro = new Otro();
        otro.obtieneDeFoo(foo);
        otro.listaNombresOtro.set(0, "Cambio");
        System.out.println(foo.listaNombres);
        System.out.println(otro.listaNombresOtro);
    }
}

Si luego quisiéramos agregar validaciones para este código, se complica la situación. Claro, en el ejemplo anterior no se puede ver porque solo hay unas 3 llamadas a Foo#listaNombres, pero si estuviese disperso a través de decenas de métodos la situación se complica. Además, también complica si se cambiase la definición del atributo (nombre, tipo, etc), por todos los cambios que se harían. Un problema adicional es que al obtener la lista de Foo#listaNombres no queremos pasar la lista directamente, en su lugar queremos una copia de ella. Podríamos hacer la copia en cada ubicación de foo.listaNombres pero eso lleva a código duplicado (bueno, multiplicado más que duplicado).


El uso de los métodos get y set (de preferencia públicos) está acorde a la especificación JavaBean:

7 Properties

Properties are discrete, named attributes of a Java Bean that can
affect its appearance or its behavior. For example, a GUI button might
have a property named “Label” that represents the text displayed in
the button.

Properties show up in a number of ways:

Properties may be exposed in scripting environments as though they
were fields of objects. So in a Javascript environment I might do
“b.Label = foo” to set the value of a property. Properties can be
accessed programmatically by other components calling their getter and
setter methods (see Section 7.1 below). (…)

7.1 Accessor methods

Properties are always accessed via method calls on their owning
object. For readable properties there will be a getter method to read
the property value. For writable properties there will be a setter
method to allow the property value to be updated.

Traducido:

7 Propiedades

Las propiedades son atributos discretos y con nombre de un Java Bean que puede afectar su apariencia o comportamiento. Por ejemplo, un botón GUI podría tener una propiedad llamada “Label” que represente el texto que se muestra en el botón.

Las propiedades se muestras de varias maneras:

Las propiedades se pueden exponer en ambientes de script como si fuesen campos de objetos. Así, en un ambiente JavaScript podría hacer “b.Label = foo” para asignar el valor de una propiedad. Las propiedades pueden ser accedidas programáticamente por otros componentes que llaman a sus métodos get y set (mirar la sección 7.1 debajo). (…)

7.1 Métodos de acceso

Siempre se accede a las propiedades a través de llamadas a métodos en el objeto que las posee. Para propiedades que se pueden leer, existirá un método get para leer el valor de la propiedad. Para propiedades que se pueden escribir, existirá un método set que permite actualizar el valor de la propiedad.

Esto significa que los métodos get y set permiten el acceso a las propiedades (atributos) de una clase. Muchos frameworks se benefician de esta definición. Por mencionar algunos:

  • Spring, CDI, Guice (IoC, inyección de dependencias)
  • Hibernate, MyBatis (acceso a datos)
  • XStream, Jackson, Gson (conversión entre objetos y JSON, XML y otros formatos)
  • Spring MVC, JSF, Expression Language, JSTL, etc (web MVC)
  • JavaFX (GUI de escritorio)
  • Metro, Apache CXF, Apache Axis (implementación/consumo de servicios Web)
  • Etc. O mejor dicho: cualquier framework que trabaje los objetos usando reflexión (reflection) que normalmente se basa en usar clases con constructores por defecto y acceso a los atributos mediante métodos get y set.

Colocando un ejemplo de Spring con configuración XML:

<bean id="fooBean" class="paquete.de.mis.clases.Foo">
    <property name="listaNombres">
        <list>
            <value>Friky</value>
            <value>Luiggi</value>
        </list>
    </property>
</bean>

La clase Java asociada a este bean:

package paquete.de.mis.clases;

public class Foo {
    private List<String> listaNombres;
    public List<String> getListaNombres() {
        return this.listaNombres;
    }
    //permite a Spring asignar el valor de la lista mediante reflexión
    public void setListaNombres(List<String> listaNombres) {
        this.listaNombres = listaNombres;
    }
}

Ejemplo en JSF para la asociación de cambpos usando Expression Language:

<!-- permite a JSF llamar al getter mediante reflexión -->
<h:dataTable value="#{foo.listaNombres}" var="nombre">
    <h:column>
        #{nombre}
    </h:column>
</h:dataTable>

La clase Java asociada:

package paquete.de.mis.managedbeans;

@ManagedBean
@ViewScoped
public class Foo {
    private List<String> listaNombres;
    @PostConstruct
    public void init() {
        listaNombres = new List<>();
        listaNombres.add("Friky");
        listaNombres.add("Luiggi");
    }
    public List<String> getListaNombres() {
        return this.listaNombres;
    }
    public void setListaNombres(List<String> listaNombres) {
        this.listaNombres = listaNombres;
    }
}

Además, lo “bueno” (¿?) de usar estos métodos es que puedes agregar lógica de negocio en ellos para evitar el contacto directo con los objetos con los que interactúas. Del caso anterior que indicamos que se quiere obtener una copia de la lista en lugar de obtener la lista directamente, podríamos agregar dicha lógica en el método get:

public class Foo {
    private List<String> listaNombres;
    public List<String> getListaNombres() {
        //en lugar de devolver la lista directamente
        //devolvemos una nueva lista con los elementos
        //de mi lista actual
        return new ArrayList<>(this.listaNombres);
    }
}

Si te parece mucho esfuerzo generar el código de estos métodos, puedes usar lombok que ofrece facilidades para agregar estos métodos mediante anotaciones en tu código, entre otros beneficios.


Adaptado de Private List with Getter/Setter vs Public List

Respondido por: user227

Leave a Reply

Your email address will not be published. Required fields are marked *