Pregunta

Estoy empezando con Java y estoy aprendiendo sobre setters, captadores y encapsulación. Tengo un programa muy simple, dos clases:

  • Container tiene una matriz int privada (numArray) con su setter y getter.

  • Main crea un objeto Container y lo utiliza en el método totalArray.


public class Container {
    private int numArray[]= {0,0,0};
    public int[] getNumArray() {
        return numArray;
    }
    public void setNumArray(int index, int value){
        numArray[index] = value;
    }    
}

public class Main {
    public static void main(String[] args) {
        Container conte = new Container();
        System.out.println(totalArray(conte.getNumArray()));
        conte.getNumArray()[2]++;
        System.out.println(totalArray(conte.getNumArray()));
    }
    private static int totalArray (int v[]){
        int total=0;
        for (int conta =0; conta<v.length;conta++){
            total+=v[conta];
        }
        return total;
    }
}

Problema: Puedo cambiar la matriz int privado a través del captador, sé que es porque getNumArray devuelve una referencia a numArray, no la propia matriz. Si estuviera interesado en un solo elemento de la matriz, me gustaría hacer un captador con un valor de índice, pero quiero que toda la matriz para el método totalArray.

¿Cómo puedo evitar que se modifique numArray de su clase?

¿Fue útil?

Solución

Todo lo que puede hacer para evitar que la gente cambiar su matriz es proporcionar una copia del mismo en el captador.

public int[] getArray() {
    return Arrays.copyOf(numArray, numArray.length);
}

De esta manera, otros métodos pueden cambiar su propia copia de la matriz, pero cuando llaman al captador de nuevo, obtener la versión original, sin cambios. Sólo el setNumArray() usted proporciona en realidad puede modificar su matriz interna.

De lo contrario, si desea bloquear completamente el recipiente, que tiene que caer matrices y utilizar un objeto inmutable. Algunas bibliotecas proporcionan listas inmutables, o utilizar Collections.unmodifiableList .

Otros consejos

Si desea devolver una matriz, que le clonarlo:

  public int[] getArray() {
       return (int[]) numArray.clone();
  }

En una API pública que debe estar seguro de documentar que claramente a las personas que llaman (en realidad de cualquier manera, si están recibiendo una matriz que va a cambiar el estado de la clase o no - que necesitan saber).

Por lo general se vería en la interfaz que está tratando de proporcionar a las personas que llaman de su clase.

Métodos como:

void addItem(Object item);
Object getItem(int index);
int getSize();

son el tipo de cosas que le proporciona en su clase de contenedor. A continuación, interactúan con la matriz privada en nombre de la persona que llama.

Si desea devolver todo el conjunto sin permitir cambios que podría en los getter copiar la matriz en una nueva y devolver la copia.

Alternativamente, si utiliza las clases de colección de Java en lugar de la matriz primitiva que proporcionan un método unmodifiableXXX () (por ejemplo Collections.unmodifiableList(myList)) que proporcionan una envoltura de sólo lectura alrededor de la colección.

La encapsulación es el proceso de ocultar la aplicación. Si la colección almacena sus datos en una matriz o no es un detalle de implementación; si se encapsuló usted quiere ser capaz de cambiar a otro tipo de almacenamiento.

El mismo hecho de que usted está exponiendo estado (o el estado derivado) como captadores y definidores rompe la encapsulación e implica que esté implementando un tipo abstracto de datos en lugar de una verdadera clase orientada a objetos. Por ejemplo, un ArrayList es un tipo de datos que no representa cualquier verdadero encapsulación de comportamiento en una aplicación. Si esto es lo que quiere depende de cómo y donde el tipo se va a utilizar.

Yo tendería a hacer que sea Container implementar Iterable<Integer> de iteración externo si se trata simplemente de un tipo de datos de contenedores, o proporcionar un método iterador interno al que se pasa a un visitante si pretende ser una clase de encapsulado. Si se trata de un tipo abstracto de datos, considere utilizar los que ya vienen implementadas como int[] o List<Integer> lugar.

Hay otra manera, lo cual no tiene una copia de la matriz, pero tiene otros inconvenientes. Lo usaría para matrices muy grandes:

private A[] items;

public List<A> getItems() {
    return Collections.unmodifiableList(Arrays.asList(items));
}
  

Cómo encapsular un array en Java

La pregunta podría ser lo suficientemente claro, sin embargo, creo que el punto de programación orientada a objetos es diseñar una clase como una entidad que manipula esta matriz y extrae su propia API.

Si persisten luego regresar a clon del array / copia defensiva es el camino a seguir para los casos simples .

Me gustaría sugerir un enfoque diferente de todas las respuestas que he visto aquí. Es útil cuando se está pensando en la encapsulación pensar también en la regla de "no preguntar un objeto por sus datos, pida un objeto para operar en sus datos para usted".

No me has dado un uso para numArray, voy a fingir que su objetivo era crear un "vector" numérico de matemáticas (no es un vector de Java) con fines de ejemplo.

Así se crea una clase NumericVector que contiene una serie de dobles. Su NumericVector tendría métodos como multiplyByScalar(double scalar) y addVector (NumericVector secondVector) para añadir elementos.

Su matriz interna está completamente encapsulado - nunca escapa. Cualquier operación que se hace en él se hace en la clase NumericVector a través de estos "métodos comerciales". ¿Cómo se puede mostrar que después de operar en él? Tener NumericVector NumericVector.toString() anulación para evitar que se imprime correctamente, o si tiene una interfaz gráfica de usuario, escribir una clase "Controller" para transferir datos desde el modelo (NumbericVector) a la vista de usuario (GUI). Esto podría requerir una manera de transmitir los elementos de su NumericVector.

Esto también indica algunas cosas que deben evitarse: No crear automáticamente las incubadoras y captadores, te rompen el encapsulamiento. A menudo se necesita captadores pero, como otros han dicho, usted debe hacer su regreso captador una versión inmutable de la matriz. Además, trate de hacer sus clases inmutable siempre que sea posible. Ese método que he mencionado anteriormente numericVector.addVector(NumericVector secondVector) probablemente no debería modificar numericVector pero devolver una nueva NumericVector con la solución.

El caso en el que esto (y en general OO) a menudo no es bibliotecas - cuando realmente ¿Quieres añadir un poco de funcionalidad a su matriz, pero todavía están saliendo como una matriz de propósito general. En este caso de Java no suele molestarse con la encapsulación en absoluto, ya que sólo añade un método de ayuda / clase que puede hacer cosas para la colección / array (mirar el objeto "matrices" para un montón de grandes ejemplos).

Una forma eficiente de la memoria de hacerlo es ...

package com.eric.stackoverflow.example;

import java.util.List;

import com.google.common.collect.ImmutableList;

public class Container {
    private int numArray[] = {0, 0, 0};
    public int[] getNumArray() {
        return ImmutableList.copyOf(numArray);
    }
    public void setNumArray(int index, int value) {
        numArray[index] = value;
    }    
}

Esto permite que el ImmutableList para establecer el número correcto de elementos de la matriz de soporte de la List y reduce el número de objetos intermedios que se crean.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top