Question

Je commence avec Java et j'apprends sur les setters, getters et encapsulation. J'ai un programme très simple, deux classes:

  • Container a un tableau int privé (numArray) avec son setter et getter.

  • Main crée un objet Container et il utilise la méthode de 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;
    }
}

Problème: Je peux changer le tableau int privé à travers le getter, je sais que ce parce getNumArray renvoie une référence à numArray, pas le tableau lui-même. Si j'étais intéressé par un seul élément du tableau, je ferais un getter avec une valeur d'index, mais je veux tout le tableau pour la méthode de totalArray.

Comment puis-je empêcher numArray d'être modifiée de sa classe?

Était-ce utile?

La solution

Tout ce que vous pouvez faire pour empêcher les gens de changer votre tableau est de fournir une copie de celui-ci dans le getter.

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

De cette façon, d'autres méthodes peuvent changer leur propre copie du tableau, mais quand ils appellent le getter à nouveau, ils obtiennent la version originale, inchangée. Seul le setNumArray() vous fournissez peut réellement modifier votre tableau interne.

Dans le cas contraire, si vous voulez bloquer complètement le conteneur, vous devez déposer des tableaux et utiliser un objet immuable. Certaines bibliothèques fournissent des listes immuables, ou utiliser Collections.unmodifiableList .

Autres conseils

Si vous voulez retourner un tableau, vous cloner:

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

Dans une API publique, vous devez être sûr de documenter que clairement aux appelants (vraiment de toute façon, si elles obtiennent un tableau qui va changer l'état de la classe ou non - ils ont besoin de savoir).

En général, vous chercheriez à l'interface que vous essayez de fournir aux appelants de votre classe.

Des méthodes telles que:

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

sont le genre de choses que vous fournissez dans votre classe de conteneur. Ils interagissent ensuite avec le tableau privé au nom de l'appelant.

Si vous voulez retourner tout le tableau sans permettre changements que vous pouvez dans le getter copier le tableau dans un nouveau et retourner la copie.

Par ailleurs, si vous utilisez les classes de collection Java au lieu du tableau primitif ils fournissent une méthode unmodifiableXXX () (par exemple Collections.unmodifiableList(myList)) qui fournissent une enveloppe de lecture seule autour de la collection.

Encapsulation est le processus de cacher la mise en œuvre. Que la collection stocke ses données dans un tableau est ou non un détail de mise en œuvre; si elle a été encapsulé que vous voulez être en mesure de le changer à un autre type de stockage.

Le fait que vous exposiez état (ou état dérivé) comme l'encapsulation des apporteurs et des pauses setters et implique que vous implémentez un type de données abstrait plutôt que d'une véritable classe orientée objet. Par exemple, un ArrayList est un type de données qui ne représente pas une véritable encapsulation du comportement dans une application. Que ce soit ce que vous voulez dépend comment et où le type doit être utilisé.

Je aurais tendance à soit faire Container mettre en œuvre Iterable<Integer> pour interation externe si elle est simplement un type de données de conteneur, ou fournir une méthode iterator interne à laquelle vous passez un visiteur si elle veut une classe encapsulée. Si elle est un type de données abstrait, fortement envisager d'utiliser les intégrés tels que int[] ou List<Integer> à la place.

Il y a une autre façon, qui ne fait pas de copie du tableau, mais a d'autres inconvénients. Je l'utiliser pour de très grands réseaux:

private A[] items;

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

Comment encapsuler un tableau en Java

La question est peut-être assez clair, mais je suppose que le point de POO est de concevoir une classe comme une entité qui manipule ce tableau et des extraits de son propre api.

Si vous persistez ensuite revenir clone du tableau / copie défensive est le chemin à parcourir pour les cas simples .

Je voudrais suggérer une approche différente de toutes les réponses que j'ai vu ici. Il est à portée de main lorsque vous pensez à l'encapsulation de penser aussi de la règle « Ne demandez pas un objet pour ses données, demander un objet à utiliser pour vous sur ses données ».

Vous ne me donne pas une utilisation pour numArray, je vais faire semblant votre objectif était de créer un « vecteur » numérique de mathématiques (pas Java Vector) à titre d'exemple.

Vous créez une classe NumericVector qui contient un tableau de doubles. Votre NumericVector aurait des méthodes telles que multiplyByScalar(double scalar) et addVector (NumericVector secondVector) pour ajouter des éléments.

Votre tableau interne est complètement encapsulé - il échappe jamais. Toute opération fait sur elle se fait dans votre classe NumericVector via ces « méthodes commerciales ». Comment afficher après exploitation sur elle? Avoir NumericVector override NumericVector.toString() il imprime correctement, ou si vous avez une interface graphique, écrire une classe « Controller » pour transférer des données à partir de votre modèle (NumbericVector) à votre vue (GUI). Cela pourrait nécessiter un moyen de diffuser des éléments de votre NumericVector.

Cela indique aussi quelques choses à éviter: Ne créez pas automatiquement setters et getters, ils cassent votre encapsulation. Vous avez souvent besoin getters mais, comme d'autres ici ont dit, vous devriez faire votre getter retourner une version immuable du tableau. Essayez également de faire vos classes immuables autant que possible. Cette méthode je l'ai mentionné plus tôt numericVector.addVector(NumericVector secondVector) devrait probablement pas modifier numericVector mais retourner une nouvelle NumericVector avec la solution.

Le cas où cela (et OO en général) échouent souvent est des bibliothèques - lorsque vous vraiment voulez ajouter un peu de fonctionnalités à votre tableau mais sont laissant comme un tableau d'usage général. Dans ce cas, Java ne dérange pas habituellement avec l'encapsulation du tout, il ajoute juste une méthode d'assistance / classe qui peut faire des choses à la collecte / array (voir l'objet « tableaux » pour un tas de bons exemples).

Une façon de faire efficace mémoire est ...

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

Cela permet à l'ImmutableList pour définir le nombre correct d'éléments de la matrice de support du List et réduit le nombre d'objets intermédiaires qui sont créées.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top