Cosa si intende per immutabile?


400

Questa potrebbe essere la domanda più stupida mai fatta, ma penso che sia abbastanza confusa per un principiante di Java.

  1. Qualcuno può chiarire cosa si intende per immutabile ?
  2. Perché è un Stringimmutabile?
  3. Quali sono i vantaggi / gli svantaggi degli oggetti immutabili?
  4. Perché un oggetto mutabile come StringBuilderpreferire a String e viceversa?

Un bell'esempio (in Java) sarà molto apprezzato.


73
Vedi, non era una domanda così stupida. Sono contento che tu l'abbia chiesto!
DOK,

2
A proposito, non penso che sia la domanda più stupida di sempre :) Penso che sia un concetto abbastanza importante da capire
Jason Coco,

1
Quando hai detto StringBuilder, non intendevi la classe mutabile StringBuffer? String e StringBuffer sono più simili nella funzione rispetto a String e StringBuilder. StringBuffer è effettivamente una stringa mutabile.
Derek Mahar,

3
Posso suggerire di aggiungere il tag "principiante" a questa domanda in modo che i programmatori nuovi a Java possano trovarlo nella ricerca di altre domande introduttive?
Derek Mahar,

Risposte:


268

Immutabile significa che una volta che il costruttore di un oggetto ha completato l'esecuzione, quell'istanza non può essere modificata.

Questo è utile perché significa che puoi passare riferimenti all'oggetto in giro, senza preoccuparti che qualcun altro cambierà il suo contenuto. Soprattutto quando si ha a che fare con la concorrenza, non ci sono problemi di blocco con oggetti che non cambiano mai

per esempio

class Foo
{
     private final String myvar;

     public Foo(final String initialValue)
     {
         this.myvar = initialValue;
     }

     public String getValue()
     {
         return this.myvar;
     }
}

Foonon deve preoccuparsi che il chiamante getValue()possa modificare il testo nella stringa.

Se immagini una classe simile a Foo, ma con un membro StringBuilderanziché Stringun membro, puoi vedere che un chiamante getValue()sarebbe in grado di modificare l' StringBuilderattributo di Fooun'istanza.

Fai anche attenzione ai diversi tipi di immutabilità che potresti trovare: Eric Lippert ha scritto un articolo di blog su questo. Fondamentalmente puoi avere oggetti la cui interfaccia è immutabile ma dietro le quinte lo stato privato mutabile reale (e quindi non può essere condiviso in modo sicuro tra thread).


3
Penso che dovresti aggiungere un costruttore arg per assegnare valore almeno una volta. Il punto del codice attuale non è chiaro poiché non c'è alcun valore da cambiare davvero :).
Georgy Bolyuba,

4
Dovresti rendere il campo di sola lettura. Rende completamente esplicito che il campo è immutabile. In questo momento è immutabile per convenzione
JaredPar

7
Il membro myVar dovrebbe essere definitivo affinché questo sia veramente immutabile.
Laz

13
Hai ragione sul fatto che myVar non è accessibile al di fuori di Foo. Tuttavia, la presenza di final indica a chiunque possa modificare la classe in futuro che il suo valore non è destinato a cambiare. Tendo a favorire l'essere il più esplicito possibile in tali circostanze.
Laz

2
Che ne dici di "I tipi di riferimento non possono essere resi immutabili solo usando la parola chiave final. Final impedisce solo la riassegnazione." da en.wikipedia.org/wiki/Immutable_object
Yousha Aleayoub

81

Un oggetto immutabile è un oggetto in cui i campi interni (o almeno tutti i campi interni che influiscono sul suo comportamento esterno) non possono essere modificati.

Ci sono molti vantaggi nelle stringhe immutabili:

Prestazioni: eseguire la seguente operazione:

String substring = fullstring.substring(x,y);

La C sottostante per il metodo substring () è probabilmente qualcosa del genere:

// Assume string is stored like this:
struct String { char* characters; unsigned int length; };

// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
    struct String* out = malloc(sizeof(struct String));
    out->characters = in->characters + begin;
    out->length = end - begin;
    return out;
}

Nota che nessuno dei personaggi deve essere copiato! Se l'oggetto String fosse mutabile (i caratteri potrebbero cambiare in seguito), dovresti copiare tutti i caratteri, altrimenti le modifiche ai caratteri nella sottostringa verrebbero riflesse nell'altra stringa in seguito.

Concorrenza: se la struttura interna di un oggetto immutabile è valida, sarà sempre valida. Non è possibile che thread diversi possano creare uno stato non valido all'interno di quell'oggetto. Quindi, gli oggetti immutabili sono Thread Safe .

Garbage collection: è molto più facile per il garbage collector prendere decisioni logiche su oggetti immutabili.

Tuttavia, ci sono anche aspetti negativi dell'immutabilità:

Performance: Aspetta, pensavo avessi detto che la performance era un vantaggio dell'immutabilità! Beh, a volte lo è, ma non sempre. Prendi il seguente codice:

foo = foo.substring(0,4) + "a" + foo.substring(5);  // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder

Le due righe sostituiscono entrambe il quarto carattere con la lettera "a". Non solo il secondo pezzo di codice è più leggibile, è anche più veloce. Guarda come dovresti fare il codice sottostante per pippo. Le sottostringhe sono facili, ma ora perché c'è già un personaggio nello spazio cinque e qualcos'altro potrebbe fare riferimento a foo, non puoi semplicemente cambiarlo; devi copiare l'intera stringa (ovviamente alcune di queste funzionalità sono astratte in funzioni nella C reale sottostante, ma il punto qui è mostrare il codice che viene eseguito tutto in un unico posto).

struct String* concatenate(struct String* first, struct String* second)
{
    struct String* new = malloc(sizeof(struct String));
    new->length = first->length + second->length;

    new->characters = malloc(new->length);

    int i;

    for(i = 0; i < first->length; i++)
        new->characters[i] = first->characters[i];

    for(; i - first->length < second->length; i++)
        new->characters[i] = second->characters[i - first->length];

    return new;
}

// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));

Nota che il concatenato viene chiamato due volte, il che significa che è necessario eseguire il loop dell'intera stringa! Confronta questo con il codice C per l' baroperazione:

bar->characters[4] = 'a';

L'operazione di stringa mutabile è ovviamente molto più veloce.

In conclusione: nella maggior parte dei casi, si desidera una stringa immutabile. Ma se devi aggiungere e inserire molte stringhe in una stringa, hai bisogno della mutabilità per la velocità. Se si desidera ottenere vantaggi in termini di sicurezza della concorrenza e garbage collection, la chiave è mantenere gli oggetti mutabili locali in un metodo:

// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
    StringBuilder mutable;
    boolean first = true;

    for(int i = 0; i < strings.length; i++)
    {
        if(!first) first = false;
        else mutable.append(separator);

        mutable.append(strings[i]);
    }

    return mutable.toString();
}

Poiché l' mutableoggetto è un riferimento locale, non devi preoccuparti della sicurezza della concorrenza (solo un thread lo tocca mai). E poiché non viene indicato in nessun altro luogo, viene allocato solo nello stack, quindi viene deallocato non appena termina la chiamata di funzione (non devi preoccuparti della garbage collection). E ottieni tutti i benefici prestazionali di mutabilità e immutabilità.


4
Ottima lettura! solo una cosa che penso dovrebbe essere se (prima) e non se (! prima)
Siddhartha,

Ciò che è necessario non è che i campi siano immutabili, ma piuttosto che lo stato osservabile definito dell'oggetto sia immutabile; un oggetto che contiene un riferimento a un altro oggetto come mezzo di incapsulamento dello stato in esso contenuto può essere immutabile solo se tutti gli aspetti incapsulati dello stato che espone al mondo esterno sono ugualmente immutabili. Si noti che non è necessario né sufficiente che i campi siano di tipo immutabile. Ciò che conta è lo stato visibile.
supercat

7
Passing pointers because Java is pass-by-referenceJava non è "pass-by-value?"
Cristian Gutu,

@CristianGutu sì hai ragione JAVA è "Passa per valore" non "Passa per RIFERIMENTO"
Arsh Kaushal,

Il riferimento viene passato come valore !!
Devv

31

In realtà String non è immutabile se usi la definizione di Wikipedia suggerita sopra.

Lo stato di String cambia post costruzione. Dai un'occhiata al metodo hashcode (). String memorizza nella cache il valore di hashcode in un campo locale ma non lo calcola fino alla prima chiamata di hashcode (). Questa valutazione pigra dell'hashcode pone String in una posizione interessante come un oggetto immutabile il cui stato cambia, ma non si può osservare che sia cambiato senza usare la riflessione.

Quindi forse la definizione di immutabile dovrebbe essere un oggetto che non si può osservare per essere cambiato.

Se lo stato cambia in un oggetto immutabile dopo che è stato creato ma nessuno può vederlo (senza riflessione) l'oggetto è ancora immutabile?


1
Buona idea - un oggetto che non si può osservare per essere cambiato, così come, nessun modo per cambiarlo dall'esterno. Il campo privato per hashCode () è una modifica interna che non è rilevante per lo stato visibile esternamente dell'oggetto.
mparaz,

2
In realtà si può osservare che è cambiato se si usa la riflessione. Vedi di più su Stringhe di Sedgewick sono mutabili se permetti la riflessione .
Miguel,

24

Gli oggetti immutabili sono oggetti che non possono essere modificati a livello di codice. Sono particolarmente utili per ambienti multi-thread o altri ambienti in cui più di un processo è in grado di alterare (mutare) i valori in un oggetto.

Solo per chiarire, tuttavia, StringBuilder è in realtà un oggetto mutabile, non immutabile. Una normale java String è immutabile (il che significa che una volta creata non è possibile modificare la stringa sottostante senza cambiare l'oggetto).

Ad esempio, supponiamo che io abbia una classe chiamata ColoredString che ha un valore String e un colore String:

public class ColoredString {

    private String color;
    private String string;

    public ColoredString(String color, String string) {
        this.color  = color;
        this.string = string;
    }

    public String getColor()  { return this.color;  }
    public String getString() { return this.string; }

    public void setColor(String newColor) {
        this.color = newColor;
    }

}

In questo esempio, si dice che ColoredString è mutabile perché è possibile modificare (mutare) una delle sue proprietà chiave senza creare una nuova classe ColoredString. Il motivo per cui questo potrebbe essere negativo è, ad esempio, supponiamo che tu abbia un'applicazione GUI che ha più thread e stai usando ColoredStrings per stampare i dati sulla finestra. Se hai un'istanza di ColoredString che è stata creata come

new ColoredString("Blue", "This is a blue string!");

Quindi ti aspetteresti che la stringa sia sempre "Blu". Se un altro thread, tuttavia, ha ottenuto questa istanza e ha chiamato

blueString.setColor("Red");

Avresti improvvisamente, e probabilmente inaspettatamente, ora una stringa "Rossa" quando volevi una "Blu". Per questo motivo, gli oggetti immutabili sono quasi sempre preferiti quando si passano istanze di oggetti. Quando hai un caso in cui gli oggetti mutabili sono veramente necessari, allora in genere proteggeresti l'objet passando solo copie dal tuo specifico campo di controllo.

Per ricapitolare, in Java, java.lang.String è un oggetto immutabile (non può essere modificato una volta creato) e java.lang.StringBuilder è un oggetto mutabile perché può essere modificato senza creare una nuova istanza.


Dovresti rendere i campi di sola lettura. In questo momento la tua classe è immutabile per convenzione. Non ci sono indicazioni per i futuri sviluppatori che l'immutabile sia intenzionale. Rendere i campi di sola lettura ti aiuterà a chiarire le tue intenzioni per un futuro dev
JaredPar

@JaredPar - In realtà, la classe non è affatto immutabile ... è un esempio di una classe mutabile per dimostrare perché potrebbe essere un problema.
Jason Coco,

1
@JaredPar - Oh, va tutto bene :) Lo riscriverei un po 'per essere più chiaro, ma quello di Douglas è già ben scritto e sembra essere il preferito, quindi lascerò il mio come altro esempio; ma qualcuno lo ha effettivamente modificato per rendere definitive le proprietà che pensavo fossero divertenti :)
Jason Coco,

24
  1. Nelle applicazioni di grandi dimensioni è comune che i letterali di stringa occupino grandi quantità di memoria. Quindi, per gestire in modo efficiente la memoria, la JVM alloca un'area chiamata "Pool di costanti di stringhe" (si noti che in memoria anche una stringa senza riferimenti porta un char [], un int per la sua lunghezza e un altro per il suo hashCode. Per un numero al contrario, sono richiesti al massimo otto byte immediati )
  2. Quando il complier incontra un valore letterale String, controlla il pool per verificare se esiste già un valore letterale identico. E se ne viene trovato uno, il riferimento al nuovo letterale viene indirizzato alla stringa esistente e non viene creato un nuovo "oggetto letterale stringa" (la stringa esistente ottiene semplicemente un riferimento aggiuntivo).
  3. Quindi: la mutabilità delle stringhe consente di risparmiare memoria ...
  4. Ma quando una qualsiasi delle variabili cambia valore, In realtà - è solo il loro riferimento che viene modificato, non il valore in memoria (quindi non influenzerà le altre variabili che lo fanno riferimento) come visto di seguito ....

String s1 = "Vecchia stringa";

//s1 variable, refers to string in memory
        reference                 |     MEMORY       |
        variables                 |                  |

           [s1]   --------------->|   "Old String"   |

String s2 = s1;

//s2 refers to same string as s1
                                  |                  |
           [s1]   --------------->|   "Old String"   |
           [s2]   ------------------------^

s1 = "Nuova stringa";

//s1 deletes reference to old string and points to the newly created one
           [s1]   -----|--------->|   "New String"   |
                       |          |                  |
                       |~~~~~~~~~X|   "Old String"   |
           [s2]   ------------------------^

La stringa originale "in memoria" non è cambiata, ma la variabile di riferimento è stata modificata in modo che faccia riferimento alla nuova stringa. E se non avessimo s2, "Old String" sarebbe ancora nella memoria ma non saremo in grado di accedervi ...


16

"immutabile" significa che non è possibile modificare il valore. Se si dispone di un'istanza della classe String, qualsiasi metodo chiamato che sembra modificare il valore creerà effettivamente un'altra stringa.

String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"

Per preservare le modifiche dovresti fare qualcosa del genere foo = foo.sustring (3);

Immutabile vs mutabile può essere divertente quando lavori con le raccolte. Pensa a cosa accadrà se usi un oggetto mutabile come chiave per la mappa e poi cambi il valore (suggerimento: pensa a equalse hashCode).


13

java.time

Potrebbe essere un po 'tardi, ma per capire cos'è un oggetto immutabile, considera il seguente esempio dalla nuova API Java 8 Date and Time ( java.time ). Come probabilmente saprai, tutti gli oggetti data di Java 8 sono immutabili, quindi nel seguente esempio

LocalDate date = LocalDate.of(2014, 3, 18); 
date.plusYears(2);
System.out.println(date);

Produzione:

2014/03/18

Questo stampa lo stesso anno della data iniziale perché il plusYears(2) restituisce un nuovo oggetto, quindi la vecchia data rimane invariata perché è un oggetto immutabile. Una volta creato, non è possibile modificarlo ulteriormente e la variabile della data lo indica ancora.

Quindi, quell'esempio di codice dovrebbe acquisire e usare il nuovo oggetto istanziato e restituito da quella chiamata plusYears.

LocalDate date = LocalDate.of(2014, 3, 18); 
LocalDate dateAfterTwoYears = date.plusYears(2);

date.toString ()… 18-03-2014

dateAfterTwoYears.toString ()… 18-03-2016


8

Mi piace molto la spiegazione della Guida allo studio SCJP Sun Certified Programmer for Java 5 .

Per rendere Java più efficiente in termini di memoria, JVM mette da parte un'area speciale di memoria chiamata "pool di costanti di stringhe". Quando il compilatore incontra un valore letterale String, controlla il pool per vedere se esiste già una stringa identica. Se viene trovata una corrispondenza, il riferimento al nuovo valore letterale viene diretto alla stringa esistente e non viene creato alcun nuovo oggetto letterale stringa.


Dovrebbe essere in grado di farlo con qualsiasi oggetto immutabile identico, ma suppongo che richiederebbe troppo tempo di esecuzione.
Zan Lynx,

8

Gli oggetti immutabili non possono cambiare stato dopo essere stati creati.

Ci sono tre ragioni principali per usare oggetti immutabili ogni volta che puoi, e tutti ti aiuteranno a ridurre il numero di bug che introduci nel tuo codice:

  • È molto più facile ragionare su come funziona il tuo programma quando sai che lo stato di un oggetto non può essere modificato con un altro metodo
  • Gli oggetti immutabili sono automaticamente thread-safe (supponendo che siano pubblicati in modo sicuro), quindi non saranno mai la causa di quei bug multithreading difficili da individuare
  • Gli oggetti immutabili avranno sempre lo stesso codice Hash, quindi possono essere usati come chiavi in ​​una HashMap (o simile). Se il codice hash di un elemento in una tabella hash dovesse cambiare, la voce della tabella verrebbe effettivamente persa, dal momento che i tentativi di trovarlo nella tabella finirebbero per guardare nel posto sbagliato. Questo è il motivo principale per cui gli oggetti String sono immutabili: vengono spesso utilizzati come chiavi HashMap.

Ci sono anche alcune altre ottimizzazioni che potresti essere in grado di fare nel codice quando sai che lo stato di un oggetto è immutabile, per esempio memorizzando nella cache l'hash calcolato, ma queste sono ottimizzazioni e quindi non sono così interessanti.


5

Un significato ha a che fare con il modo in cui il valore è archiviato nel computer. Ad esempio, per una stringa .Net, significa che la stringa in memoria non può essere modificata, Quando pensi di cambiarla, stai di fatto creando una nuova stringa in memoria e puntando la variabile esistente (che è solo un puntatore alla raccolta effettiva di caratteri da qualche altra parte) alla nuova stringa.


4
String s1="Hi";
String s2=s1;
s1="Bye";

System.out.println(s2); //Hi  (if String was mutable output would be: Bye)
System.out.println(s1); //Bye

s1="Hi": è s1stato creato un oggetto con il valore "Hi".

s2=s1 : s2viene creato un oggetto con riferimento all'oggetto s1.

s1="Bye": il s1valore dell'oggetto precedente non cambia perché s1ha tipo String e String type è un tipo immutabile, invece il compilatore crea un nuovo oggetto String con valore "Bye" e vi fa s1riferimento. qui quando stampiamo il s2valore, il risultato sarà "Ciao", non "Ciao" perché s2riferito s1all'oggetto precedente che aveva il valore "Ciao".


puoi aggiungere una piccola spiegazione per favore?
minigeek

3

Immutabile significa che una volta creato l'oggetto, nessuno dei suoi membri cambierà. Stringè immutabile poiché non è possibile modificarne il contenuto. Per esempio:

String s1 = "  abc  ";
String s2 = s1.trim();

Nel codice sopra, la stringa s1 non è cambiata, un altro oggetto ( s2) è stato creato usando s1.


3

Immutabile significa semplicemente immutabile o immodificabile. Una volta creato l'oggetto stringa, i suoi dati o lo stato non possono essere modificati

Considera il seguente esempio,

class Testimmutablestring{  
  public static void main(String args[]){  
    String s="Future";  
    s.concat(" World");//concat() method appends the string at the end  
    System.out.println(s);//will print Future because strings are immutable objects  
  }  
 }  

Diamo un'idea considerando il diagramma seguente,

inserisci qui la descrizione dell'immagine

In questo diagramma, puoi vedere il nuovo oggetto creato come "Future World". Ma non cambiare "Futuro". Because String is immutable. s, fare ancora riferimento a "Futuro". Se devi chiamare "Future World",

String s="Future";  
s=s.concat(" World");  
System.out.println(s);//print Future World

Perché gli oggetti stringa sono immutabili in Java?

Perché Java usa il concetto di stringa letterale. Supponiamo che ci siano 5 variabili di riferimento, tutte si riferiscono a un oggetto "Futuro". Se una variabile di riferimento modifica il valore dell'oggetto, sarà influenzata da tutte le variabili di riferimento. Ecco perché gli oggetti stringa sono immutabili in Java.


2

Una volta instanciato, non può essere modificato. Considera una classe di cui un'istanza potrebbe essere utilizzata come chiave per una tabella hash o simile. Scopri le migliori pratiche Java.


0

Oggetti immutabili

Un oggetto è considerato immutabile se il suo stato non può cambiare dopo che è stato costruito. La massima dipendenza da oggetti immutabili è ampiamente accettata come una solida strategia per la creazione di codice semplice e affidabile.

Gli oggetti immutabili sono particolarmente utili in applicazioni simultanee. Poiché non possono cambiare stato, non possono essere danneggiati dall'interferenza del thread o osservati in uno stato incoerente.

I programmatori sono spesso riluttanti a impiegare oggetti immutabili, perché si preoccupano dei costi di creazione di un nuovo oggetto invece di aggiornare un oggetto sul posto. L'impatto della creazione di oggetti è spesso sopravvalutato e può essere compensato da alcune delle efficienze associate agli oggetti immutabili. Questi includono un sovraccarico ridotto a causa della garbage collection e l'eliminazione del codice necessario per proteggere gli oggetti mutabili dalla corruzione.

Le seguenti sottosezioni prendono una classe le cui istanze sono mutabili e ne derivano una classe con istanze immutabili. In tal modo, danno regole generali per questo tipo di conversione e dimostrano alcuni dei vantaggi degli oggetti immutabili.

fonte


0

Poiché la risposta accettata non risponde a tutte le domande. Sono costretto a dare una risposta dopo 11 anni e 6 mesi.

Qualcuno può chiarire cosa si intende per immutabile?

Spero che tu intendessi un oggetto immutabile (perché potremmo pensare a un riferimento immutabile ).

Un oggetto è immutabile : se creato una volta, rappresentano sempre lo stesso valore (non ha alcun metodo che cambi il valore).

Perché è un Stringimmutabile?

Rispettare la definizione sopra che può essere verificata esaminando il codice sorgente Sting.java .

Quali sono i vantaggi / gli svantaggi degli oggetti immutabili? i tipi immutabili sono:

  • più sicuro dai bug.

  • più facile da capire.

  • e altro pronto per il cambiamento.

Perché un oggetto mutabile come StringBuilder dovrebbe essere preferito rispetto a String e viceversa?

Restringere la domanda Perché abbiamo bisogno del StringBuilder mutabile nella programmazione? Un uso comune è concatenare un gran numero di stringhe insieme, in questo modo:

String s = "";
for (int i = 0; i < n; ++i) {
    s = s + n;
}

Usando stringhe immutabili, questo fa molte copie temporanee - il primo numero della stringa ("0") viene effettivamente copiato n volte nel corso della creazione della stringa finale, il secondo numero viene copiato n-1 volte, e così su. In realtà costa O (n2) tempo solo per fare tutte quelle copie, anche se abbiamo solo concatenato n elementi.

StringBuilder è progettato per ridurre al minimo questa copia. Utilizza una struttura di dati interna semplice ma intelligente per evitare di fare qualsiasi copia fino alla fine, quando chiedi la stringa finale con una chiamata toString ():

StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) {
  sb.append(String.valueOf(n));
}
String s = sb.toString();

Ottenere buone prestazioni è uno dei motivi per cui utilizziamo oggetti mutabili. Un'altra è la condivisione conveniente: due parti del programma possono comunicare più comodamente condividendo una struttura di dati mutabili comune.

Ulteriori informazioni sono disponibili qui: https://web.mit.edu/6.005/www/fa15/classes/09-immutability/#useful_immutable_types


-1

Un oggetto immutabile è quello che non puoi modificare dopo averlo creato. Un esempio tipico sono letterali stringa.

Il linguaggio di programmazione AD, che diventa sempre più popolare, ha una nozione di "immutabilità" attraverso la parola chiave "invariante". Leggi questo articolo di Dr.Dobb al riguardo - http://dobbscodetalk.com/index.php?option=com_myblog&show=Invariant-Strings.html&Itemid=29 . Spiega perfettamente il problema.


Credo che da D 2.020 la parola chiave sia stata modificata da invariante a immutabile. Non vedo un punto, ma dice "l'immutabile ora è implementato". digitalmars.com/d/2.0/changelog.html#new2_020
he_the_great
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.