Classe Interna Java

La classe interna di Java è definita all’interno del corpo di un’altra classe. La classe interna di Java può essere dichiarata private, public, protected o con accesso di default, mentre una classe esterna può avere solo accesso public o di default. Le classi annidate di Java sono divise in due tipi.

  1. classe annidata statica

    Se la classe annidata è statica, allora viene chiamata classe annidata statica. Le classi annidate statiche possono accedere solo ai membri statici della classe esterna. Una classe annidata statica è la stessa di qualsiasi altra classe di primo livello e viene annidata solo per comodità di impacchettamento. Un oggetto di classe statica può essere creato con la seguente istruzione.

    OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass(); 
  2. Classe interna di Java

    Qualsiasi classe nidificata non statica è nota come classe interna in Java. La classe interna di Java è associata all’oggetto della classe e può accedere a tutte le variabili e i metodi della classe esterna. Poiché le classi interne sono associate all’istanza, non possiamo avere variabili statiche al loro interno. L’oggetto della classe interna di Java fa parte dell’oggetto della classe esterna e per creare un’istanza della classe interna, prima dobbiamo creare un’istanza della classe esterna. La classe interna di Java può essere istanziata in questo modo;

    OuterClass outerObject = new OuterClass();
    OuterClass.InnerClass innerObject = outerObject.new InnerClass();
    

Ci sono due tipi speciali di classi interne Java.

  1. classe interna locale

    Se una classe è definita all’interno di un corpo di un metodo, è nota come classe interna locale. Poiché la classe interna locale non è associata all’oggetto, non possiamo utilizzare i modificatori di accesso privato, pubblico o protetto con essa. Gli unici modificatori consentiti sono astratto o finale. Una classe interna locale può accedere a tutti i membri della classe esterna e alle variabili locali finali nello scope in cui è definita. Inoltre, può anche accedere a una variabile locale non finale del metodo in cui è definita, ma non può modificarla. Quindi, se si tenta di stampare il valore di una variabile locale non finale, sarà consentito, ma se si tenta di cambiarne il valore dall’interno del metodo della classe interna locale, si otterrà un errore in fase di compilazione. Una classe interna locale può essere definita come segue:

    package com.journaldev.innerclasses;
    
    public class MainClass {
    
    	private String s_main_class;
    
    	public void print() {
    		String s_print_method = "";
    		// classe interna locale all'interno del metodo
    		class Logger {
    			// in grado di accedere alle variabili della classe esterna
    			String name = s_main_class; 
    			// in grado di accedere alle variabili del metodo non finali
    			String name1 = s_print_method; 
    
    			public void foo() {
    				String name1 = s_print_method;
    				// Il codice seguente genererà un errore in fase di compilazione:
    				// La variabile locale s_print_method definita in uno scope esterno deve essere finale o effettivamente finale
    				// s_print_method= ":";
    			}
    		}
    		// istanziare la classe interna locale nel metodo per utilizzarla
    		Logger logger = new Logger();
    
    	}
    }
    

    Possiamo definire una classe interna locale all’interno di qualsiasi blocco, come un blocco statico, un blocco if-else, ecc. Tuttavia, in questo caso, lo scope della classe sarà molto limitato.

    public class MainClass {
    
    	static {
    		class Foo {
    			
    		}
    		Foo f = new Foo();
    	}
    	
    	public void bar() {
    		if(1 < 2) {
    			class Test {
    				
    			}
    			Test t1 = new Test();
    		}
    		// Il seguente codice genererà un errore a causa dello scope della classe
    		//Test t = new Test();
    		//Foo f = new Foo();
    	}
    }
    
  2. classe interna anonima

    Una classe interna anonima senza nome è conosciuta come classe interna anonima. Una classe anonima viene definita e istanziata in un’unica istruzione. La classe interna anonima estende sempre una classe o implementa un’interfaccia. Poiché una classe anonima non ha un nome, non è possibile definire un costruttore per una classe anonima. Le classi interne anonime sono accessibili solo nel punto in cui sono definite. È un po’ difficile definire come creare una classe interna anonima, vedremo l’utilizzo in tempo reale nel programma di test qui sotto.

Ecco una classe Java che mostra come definire una classe interna Java, una classe interna statica, una classe interna locale e una classe interna anonima. OuterClass.java

package com.journaldev.nested;

import java.io.File;
import java.io.FilenameFilter;

public class OuterClass {
    
    private static String name = "OuterClass";
    private int i;
    protected int j;
    int k;
    public int l;

    //Costruttore di OuterClass

    public OuterClass(int i, int j, int k, int l) {
        this.i = i;
        this.j = j;
        this.k = k;
        this.l = l;
    }

    public int getI() {
        return this.i;
    }

    //Classe annidata statica, può accedere alle variabili/metodi statici di OuterClass

    static class StaticNestedClass {
        private int a;
        protected int b;
        int c;
        public int d;

        public int getA() {
            return this.a;
        }

        public String getName() {
            return name;
        }
    }

    //Classe interna, non statica e può accedere a tutte le variabili/metodi della classe esterna

    class InnerClass {
        private int w;
        protected int x;
        int y;
        public int z;

        public int getW() {
            return this.w;
        }

        public void setValues() {
            this.w = i;
            this.x = j;
            this.y = k;
            this.z = l;
        }

        @Override
        public String toString() {
            return "w=" + w + ":x=" + x + ":y=" + y + ":z=" + z;
        }

        public String getName() {
            return name;
        }
    }

    //Classe interna locale

    public void print(String initial) {
        //Classe interna locale
 inside the method
        class Logger {
            String name;

            public Logger(String name) {
                this.name = name;
            }

            public void log(String str) {
                System.out.println(this.name + ": " + str);
            }
        }

        Logger logger = new Logger(initial);
        logger.log(name);
        logger.log("" + this.i);
        logger.log("" + this.j);
        logger.log("" + this.k);
        logger.log("" + this.l);
    }

    //Classe interna anonima

    public String[] getFilesInDir(String dir, final String ext) {
        File file = new File(dir);
        //Classe interna anonima
 implementing FilenameFilter interface
        String[] filesList = file.list(new FilenameFilter() {

            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(ext);
            }

        });
        return filesList;
    }
}

Ecco il programma di test che mostra come istanziare e utilizzare la classe interna in Java. InnerClassTest.java

package com.journaldev.nested;

import java.util.Arrays;
//Le classi annidate possono essere utilizzate nell'import per una facile istanziazione

import com.journaldev.nested.OuterClass.InnerClass;
import com.journaldev.nested.OuterClass.StaticNestedClass;

public class InnerClassTest {

    public static void main(String[] args) {
        OuterClass outer = new OuterClass(1,2,3,4);
        
        //Esempio di classe annidata statica

        StaticNestedClass staticNestedClass = new StaticNestedClass();
        StaticNestedClass staticNestedClass1 = new StaticNestedClass();
        
        System.out.println(staticNestedClass.getName());
        staticNestedClass.d=10;
        System.out.println(staticNestedClass.d);
        System.out.println(staticNestedClass1.d);
        
        //Esempio di classe interna

        InnerClass innerClass = outer.new InnerClass();
        System.out.println(innerClass.getName());
        System.out.println(innerClass);
        innerClass.setValues();
        System.out.println(innerClass);
        
        //Chiamata del metodo utilizzando la classe interna locale

        outer.print("Outer");
        
        //Chiamata del metodo utilizzando la classe interna anonima

        System.out.println(Arrays.toString(outer.getFilesInDir("src/com/journaldev/nested", ".java")));
        
        System.out.println(Arrays.toString(outer.getFilesInDir("bin/com/journaldev/nested", ".class")));
    }

}

Ecco l’output del programma di esempio di classe interna Java sopra.

OuterClass
10
0
OuterClass
w=0:x=0:y=0:z=0
w=1:x=2:y=3:z=4
Outer: OuterClass
Outer: 1
Outer: 2
Outer: 3
Outer: 4
[NestedClassTest.java, OuterClass.java]
[NestedClassTest.class, OuterClass$1.class, OuterClass$1Logger.class, OuterClass$InnerClass.class, OuterClass$StaticNestedClass.class, OuterClass.class]

Si noti che quando OuterClass viene compilata, vengono creati file di classe separati per la classe interna, la classe interna locale e la classe annidata statica.

Vantaggi della classe interna Java

  1. Se una classe è utile solo a una classe, ha senso tenerla annidata e insieme. Aiuta nell’organizzazione delle classi.
  2. Le classi interne di Java implementano l’incapsulamento. Nota che le classi interne possono accedere ai membri privati della classe esterna e allo stesso tempo possiamo nascondere la classe interna dal mondo esterno.
  3. Mantenere la classe piccola all’interno delle classi di primo livello posiziona il codice più vicino a dove viene utilizzato e rende il codice più leggibile e manutenibile.

Questo è tutto per la classe interna di Java.

Source:
https://www.digitalocean.com/community/tutorials/java-inner-class