Domanda

Sto iniziando con Java e sto imparando circa setter, getter e incapsulamento. Ho un programma molto semplice, due classi:

  • Container ha un int matrice privata (numArray) con il suo setter e getter.

  • Main crea un oggetto Container e lo utilizza in modo 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: posso cambiare la matrice private int attraverso il getter, so che è perché getNumArray restituisce un riferimento a numArray, non la matrice stessa. Se fossi interessato a un singolo elemento dell'array, mi piacerebbe fare un getter con un valore di indice, ma voglio l'intera matrice per il metodo totalArray.

Come posso impedire che numArray venga modificato dalla sua classe?

È stato utile?

Soluzione

Tutto quello che può fare per impedire alle persone di cambiare la matrice è di fornire una copia di esso nel getter.

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

In questo modo, altri metodi possono cambiare la propria copia della matrice, ma quando chiamano di nuovo il getter, ottengono la versione originale, inalterato. Solo il setNumArray() che fornisci può effettivamente modificare la matrice interna.

In caso contrario, se si vuole bloccare completamente il contenitore, si deve cadere array e utilizzare un oggetto immutabile. Alcune librerie forniscono elenchi immutabili, o utilizzare Collections.unmodifiableList .

Altri suggerimenti

Se si desidera restituire un array, si dovrebbe clonarlo:

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

In un'API pubblica si dovrebbe essere sicuri di documentare che chiaramente ai chiamanti (in realtà in entrambi i casi, se stanno ottenendo una matrice che cambierà lo stato della classe o no - hanno bisogno di sapere).

In genere si dovrebbe guardare a livello di interfaccia si sta cercando di fornire ai chiamanti della vostra classe.

Metodi come:

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

sono il genere di cose che si dovrebbero fornire nella classe contenitore. Hanno poi interagiscono con la matrice privata in nome del chiamante.

Se si desidera restituire l'intero array senza consentire modifiche potrebbe in getter copiare l'array in una nuova e restituire la copia.

In alternativa, se si utilizzano le classi Java raccolta invece della matrice primitiva forniscono un metodo unmodifiableXXX () (per esempio Collections.unmodifiableList(myList)) che forniscono un involucro di sola lettura intorno alla collezione.

L'incapsulamento è il processo di nascondere l'implementazione. Se la raccolta memorizza i propri dati in una matrice o non è un dettaglio di implementazione; se è stato incapsulato si vorrebbe essere in grado di cambiare ad un altro tipo di archiviazione.

Il fatto che si espongono stato (o stato derivato) come getter e setter pause incapsulamento implica e si implementa un tipo di dato astratto piuttosto che una vera classe orientato agli oggetti. Ad esempio, un ArrayList è un tipo di dati che non rappresenta una vera incapsulamento di comportamento in un'applicazione. Se questo è ciò che si desidera dipende da come e dove il tipo deve essere utilizzato.

tenderei a uno rendere Container implementare Iterable<Integer> per interation esterno se si tratta semplicemente di un tipo di dati del contenitore, o fornire un metodo iteratore interno al quale si passa un visitatore se inteso come una classe incapsulato. Se si tratta di un tipo di dato astratto, considerare vivamente di utilizzare quelle built-in, come int[] o List<Integer> posto.

C'è ancora un altro modo, che non ha una copia della matrice, ma presenta altri inconvenienti. Io lo uso per le matrici molto grandi:

private A[] items;

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

Come per incapsulare un array in Java

La domanda potrebbe essere abbastanza chiaro, tuttavia credo che il punto di OOP è quello di progettare una classe come entità che manipola questo array ed estrae il proprio api.

Se persistono poi tornare a clone del array / copia difensiva è la strada da percorrere per i casi semplici .

Vorrei suggerire un approccio diverso da tutte le risposte che ho visto qui. E 'utile quando si sta pensando di incapsulamento di pensare anche della regola "Non chiedere un oggetto per i suoi dati, chiedere un oggetto per operare sui propri dati per voi".

Non mi hai dato un uso per numArray, ho intenzione di far finta il tuo obiettivo era quello di creare un "vettore" numerica dalla matematica (non un vettore Java) a scopo di esempio.

Quindi, si crea una classe NumericVector che contiene una serie di doppie. Il tuo NumericVector avrebbe metodi come multiplyByScalar(double scalar) e addVector (NumericVector secondVector) per aggiungere elementi.

Il tuo array interno è completamente incapsulato - non è mai sfugge. Qualsiasi operazione fatta su di esso viene fatto nella classe NumericVector tramite questi "metodi commerciali". Come si fa a visualizzare dopo che operano su di esso? Avere NumericVector esclusione NumericVector.toString() così esso stampa correttamente, o se si dispone di una GUI, scrivere una classe "Controller" per trasferire i dati dal modello (NumbericVector) per la visualizzazione (GUI). Ciò potrebbe richiedere un modo per trasmettere gli elementi dal vostro NumericVector.

Questo indica anche alcune cose da evitare: Non creare automaticamente setter e getter, si rompono il tuo incapsulamento. Spesso è necessario getter, ma, come altri qui hanno detto, si dovrebbe rendere il vostro getter restituire una versione immutabile di matrice. Anche cercare di rendere le vostre classi immutabili, ove possibile. Questo metodo che ho già detto in precedenza numericVector.addVector(NumericVector secondVector) non dovrebbe probabilmente modificare numericVector ma restituire una nuova NumericVector con la soluzione.

Il caso in cui questo (e OO in generale) spesso non è librerie - quando si davvero desidera aggiungere un po 'di funzionalità per l'array, ma sono ancora lasciandolo come un array di uso generale. In questo caso Java di solito non perdere tempo con incapsulamento a tutti, si aggiunge solo un metodo di supporto / classe in grado di fare le cose per la raccolta / array (guardare l'oggetto "array" per un gruppo di grandi esempi).

Un modo efficiente della memoria per farlo è ...

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;
    }    
}

Questo permette al ImmutableList per impostare il corretto numero di elementi nella matrice sostegno del List e riduce il numero di oggetti intermedi che vengono creati.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top