2010-01-05 95 views
8

Estoy comenzando con Java y estoy aprendiendo sobre setters, getters y encapsulación. Tengo un programa muy simple, dos clases:Cómo encapsular una matriz en Java

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

  • 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, obtendría un getter con un valor de índice, pero quiero la matriz completa para el método totalArray.

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

Respuesta

8

Todo lo que puede hacer para evitar que las personas cambien su matriz es proporcionar una copia 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. Solo el setNumArray() que proporciona puede modificar realmente su matriz interna.

De lo contrario, si desea bloquear completamente el contenedor, tiene que soltar matrices y usar un objeto inmutable. Las bibliotecas Some proporcionan listas inmutables o usan Collections.unmodifiableList.

+0

'Algo' como en la biblioteca estándar de Java. –

+1

@Pete: sí, pero no solo. – glmxndr

3

Normalmente, miraría 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. Luego interactúan con la matriz privada en nombre de la persona que llama.

Si desea devolver toda la matriz sin permitir cambios, puede en el getter copiar la matriz en una nueva y devolver la copia.

Alternativamente, si utiliza las clases de colección Java en lugar de la matriz primitiva, proporcionan un método no modificable de XXX() (por ejemplo, Collections.unmodifiableList(myList)) que proporciona un contenedor de solo lectura alrededor de la colección.

+1

Por favor, corrija los nombres de sus métodos. Los métodos de Java comienzan con letras minúsculas y Object es una clase (mayúsculas). –

+0

Gracias, esto es lo que sucede cuando pasas un día mirando C# ...;) – Paolo

+1

+1: ¡Encapsula tu matriz ** sin exponer la existencia de la matriz **! Haga que el contenedor tenga una interfaz completa que represente las funciones reales de esa clase, en lugar de simplemente devolver sus miembros de datos internos a los llamantes para que hagan lo que deseen. –

6

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 cualquier manera, si están recibiendo una matriz eso cambiará el estado de la clase o no, ellos necesitan saber).

0

La encapsulación es el proceso de ocultar la implementación.Si la colección almacena sus datos en una matriz o no es un detalle de implementación; si estaba encapsulado, le gustaría poder cambiarlo a otro tipo de almacenamiento.

El mero hecho de que esté exponiendo estado (o estado derivado) como getters y setters rompe la encapsulación e implica que está implementando un tipo de datos abstractos en lugar de una verdadera clase orientada a objetos. Por ejemplo, un ArrayList es un tipo de datos que no representa ninguna encapsulación verdadera del comportamiento en una aplicación. Si esto es lo que quiere depende de cómo y dónde se utilizará el tipo.

Tendería a hacer Container implementar Iterable<Integer> para interar externo si se trata simplemente de un tipo de datos de contenedor, o proporcionar un método de iterador interno al que le pase un visitante si pretendía ser una clase encapsulada. Si se trata de un tipo de datos abstracto, considere utilizar los integrados, como int[] o List<Integer>.

0

Hay otra manera, que no hace 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)); 
} 
0

Me gustaría sugerir un enfoque diferente de todas las respuestas que vi aquí. Es útil cuando piensas en la encapsulación para pensar también en la regla "No preguntes a un objeto por sus datos, pídele a un objeto que opere sobre sus datos por ti".

No me ha dado un uso para numArray, voy a pretender que su objetivo era crear un "Vector" numérico a partir de las matemáticas (no un Vector de Java) por ejemplo.

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

Su matriz interna está completamente encapsulada, nunca se escapa. Cualquier operación realizada en él se realiza en su clase NumericVector a través de estos "métodos comerciales". ¿Cómo se muestra después de operar en él? Haga que NumericVector anule NumericVector.toString() para que se imprima correctamente, o si tiene una GUI, escriba una clase "Controlador" para transferir datos de su Modelo (NumbericVector) a su vista (GUI). Esto podría requerir una forma de transmitir elementos desde su NumericVector.

Esto también indica algunas cosas que debe evitar: No cree automáticamente setters y getters, estos rompen su encapsulación. A menudo necesitas getters pero, como han dicho otros aquí, debes hacer que getter devuelva una versión inmutable de la matriz. También intente que sus clases sean inmutables siempre que sea posible. El método que mencioné anteriormente numericVector.addVector(NumericVector secondVector) probablemente no debería modificar numericVector, pero devuelve un nuevo NumericVector con la solución.

El caso en el que esto (y OO en general) a menudo fallan es bibliotecas - cuando realmente desea agregar un poco de funcionalidad a su matriz pero todavía lo deja como una matriz de propósito general. En este caso, Java generalmente no se molesta con la encapsulación, solo agrega un método/clase auxiliar que puede hacer cosas a la colección/matriz (mira el objeto "Arrays" para ver un montón de excelentes ejemplos).

0

cómo encapsular un array en Java

La pregunta podría ser lo suficientemente claro, sin embargo supongo que el punto de POO es diseñar una clase como una entidad que manipula esta matriz y extrae su propia API

Si persisten después vuelve un clon de la gama /copia defensiva es el camino a seguir para los casos simples .

0

Una forma eficiente de memoria para hacer esto 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 creó .

Cuestiones relacionadas