danilo.pianini@unibo.itgianluca.aguzzi@unibo.itangelo.filaseta@unibo.itCompiled on: 2025-12-05 — versione stampabile
A, chiamata outer è possibile innestare la definizione di un’altra classe B, chiamata innestata (statica) – in inglese, static nestedB viene quindi vista come se fosse una proprietà statica di A
A, come le new e le chiamate statiche// situazione di partenza
class A {...}
class B {...}
// modifica, usando le inner class
class A {
...
static class B { .. }
}
OuterOuter.A, Outer.B, Outer.I, Outer.A.Cclass Outer {
...
static class A { ... static class C { ... } ... }
static class B { ... }
interface I { ... } // static è implicito nelle interfacce
}
Outer.StaticNested
Outer si può accedere anche direttamente con StaticNestedOuter di StaticNested segue le regole del suo modificatore d’accessoprivateclass Outer {
...
static class StaticNested {
...
}
}
..
Outer.StaticNested obj = new Outer.StaticNested(...);
Vi sono situazioni in cui per risolvere un singolo problema è opportuno generare più classi, e non si vuole affiancarle solo come classi dello stesso package
privateCounter che modella un contatoreBidirectionalCounter, sempre un contatore, ma che può incrementare di più step alla voltaMultiCounter, sempre un contatore, ma che conta anche all’indietroCounter e specializzazioni innestatepackage it.unibo.nested;
public class Counter {
private int value;
public Counter(int initialValue) { this.value = initialValue; }
public void increment() { this.value++; }
public int getValue() { return this.value; }
public static class Multi extends Counter {
public Multi(int initialValue) { super(initialValue); }
public void multiIncrement(int n) { super.value += n; }
}
public static class Bidirectional extends Counter {
public Bidirectional(int initialValue) { super(initialValue); }
public void decrement() { super.value--; }
}
}
Counter e specializzazioni innestatepackage it.unibo.nested;
import java.util.ArrayList;
import java.util.List;
public class UseCounter {
static void main() {
final List<Counter> list = new ArrayList<>();
list.add(new Counter(100));
list.add(new Counter.Bidirectional(100));
list.add(new Counter.Multi(100));
for (final Counter c : list) {
c.increment();
}
}
}
In una classe potrebbero servire sotto-comportamenti che debbano: implementare una data interfaccia o estendere una data classe, ma che non vogliamo esporre come classi esterne
Range, un Iterable che deve quindi produrre un Iterator
RangeIterator pubblicamenteRangeSpesso classi di questo tipo non devono essere visibili dall’esterno, quindi vengono indicate come private
Range e suo RangeIterator innestatopublic class RangeNested implements Iterable<Integer> {
private final int start;
private final int stop;
public RangeNested(final int start, final int stop) {
this.start = start;
this.stop = stop;
}
public Iterator<Integer> iterator() { return new RangeIterator(this.start, this.stop); }
private static class RangeIterator implements Iterator<Integer> {
private int current;
private final int stop;
public RangeIterator(final int start, final int stop) {
this.current = start;
this.stop = stop;
}
public Integer next() { return this.current++; }
public boolean hasNext() { return this.current <= this.stop; }
public void remove() { throw new UnsupportedOperationException(); }
}
}
Map, Map.Entrypublic interface Map<K,V> {
// Query Operations
int size();
boolean isEmpty();
boolean containsKey(Object key); // usa Object.equals
boolean containsValue(Object value); // usa Object.equals
V get(Object key); // accesso a valore
// Modification Operations
V put(K key, V value); // inserimento chiave-valore
V remove(Object key); // rimozione chiave(-valore)
// Bulk Operations
void putAll(Map<? extends K, ? extends V> m);
void clear(); // cancella tutti
// Views
Set<K> keySet(); // set di chiavi
Collection<V> values(); // collezione di valori
Set<Map.Entry<K, V>> entrySet(); // set di chiavi-valore
interface Entry<K,V> {...} // public static implicito!
}
Outer.StaticNestedOuter e StaticNested sono co-locate: si vedono le proprietà privatepublicOut.C1, Out.C2,..private – è il caso più frequenteMap.EntryMap.EntryMap.EntryMap.Entrypublic interface Map<K,V> {
...
Set<Map.Entry<K, V>> entrySet();
interface Entry<K,V> { // public e static implicite!
K getKey();
V getValue();
V setValue(V value);
}
}
Outer, è possibile innestare la definizione di un’altra classe InnerClass, senza indicazione staticInnerClass è vista come se fosse una proprietà non-statica di Outer al pari di altri campi o metodiInnerClass ha sempre un riferimento ad una istanza di Outer (enclosing instance) che ne rappresenta il contesto, accessibile con la sintassi Outer.this, e ai suoi campi (privati)class Outer {
...
class InnerClass { // Nota.. non è static!
...
// ogni oggetto di InnerClass avrà un riferimento ad
// un oggetto di Outer, denominato Outer.this
}
}
public class Outer {
private final int i;
public Outer(int i) {
this.i = i;
}
public Inner createInner() {
return new Inner();
// oppure: return this.new Inner();
}
public class Inner {
private int j = 0;
public void update() {
this.j = this.j + Outer.this.i; // si usa l'oggetto di outer
}
public int getValue() {
return this.j;
}
}
}
Inner e Outerpublic class UseOuter {
static void main() {
Outer o = new Outer(5);
Outer.Inner in = o.new Inner();
System.out.println(in.getValue()); // 0
in.update();
in.update();
System.out.println(in.getValue()); // 5
Outer.Inner in2 = new Outer(10).createInner();
in2.update();
in2.update();
System.out.println(in2.getValue()); // 20
}
}
<obj-outer>.new <classe-inner>(<args>)<obj-outer> è omettibile quando sarebbe this)<classe-outer>.thisprivateRange già vista usa una static nested class
start e stop tramite l’enclosing instance!Rangepublic class RangeInner implements Iterable<Integer> {
private final int start;
private final int stop;
public RangeInner(final int start, final int stop) {
this.start = start;
this.stop = stop;
}
public Iterator<Integer> iterator() {
return this.new RangeIterator();
}
private class RangeIterator implements Iterator<Integer> {
private int current;
public RangeIterator() {
this.current = RangeInner.this.start; // this.current = start
}
public Integer next() { return this.current++; }
public boolean hasNext() {
// Accesso diretto ai campi non-statici della classe esterna!
return this.current <= RangeInner.this.stop; // Equivalente: return current <= stop;
}
public void remove() { throw new UnsupportedOperationException(); }
}
}
Outer, è possibile innestare la definizione di un’altra classe LocalClassLocalClass è a tutti gli effetti una inner class (e quindi ha enclosing instance)
static locali (innestate in un metodo)LocalClass “vede” anche le variabili nello scope del metodo in cui è definita, usabili solo se final
class Outer {
// ...
void m(final int x){
final String s = /* ... */;
class LocalClass { // Nota.. non è static!
// ... può usare Outer.this, s e x
}
LocalClass c = new LocalClass(...);
}
}
Range tramite classe localepublic class RangeLocalClass implements Iterable<Integer> {
private final int start;
private final int stop;
public RangeLocalClass(final int start, final int stop) {
this.start = start;
this.stop = stop;
}
public java.util.Iterator<Integer> iterator() {
class RangeIterator implements Iterator<Integer> {
private int current;
public RangeIterator() { this.current = RangeLocalClass.this.start; }
public Integer next() { return this.current++; }
public boolean hasNext() { return this.current <= RangeLocalClass.this.stop; }
public void remove() { }
}
return new RangeIterator();
}
}
new, è possibile innestare la definizione di un’altra classe senza indicarne il nome
final (o di fatto finali) nello scope del metodo in cui è definitaclass C {
// ...
Object m(final int x) {
return new Object() {
public String toString() { return "Valgo " + x; }
}
}
}
Range tramite classe anonima – la soluzione ottimaleimport java.util.Iterator;
public class RangeAnonymous implements Iterable<Integer> {
private final int start;
private final int stop;
public RangeAnonymous(final int start, final int stop) {
this.start = start;
this.stop = stop;
}
public Iterator<Integer> iterator() {
return new Iterator<>() {
// Non ci può essere costruttore!
private int current = start; // o anche Range4.this.start
public Integer next() {
return current++;
}
public boolean hasNext() {
return current <= stop; // o anche Range4.this.stop
}
public void remove() { }
}; // questo è il ; del return
}
}
Comparableimport java.util.Comparator;
import java.util.List;
public class UseSort {
static void main() {
final List<Integer> list = Arrays.asList(10, 40, 7, 57, 13, 19, 21, 35);
System.out.println(list);
// classe anonima a partire da una interfaccia
Collections.sort(list, new Comparator<Integer>() {
public int compare(Integer a, Integer b) {
return Integer.compare(a, b);
}
});
System.out.println(list);
Collections.sort(list, new Comparator<Integer>() {
public int compare(Integer a, Integer b) {
return Integer.compare(b, a);
}
});
System.out.println(list);
}
}
public class UseSortLambda {
static void main() {
final List<Integer> list = Arrays.asList(10, 40, 7, 57, 13, 19, 21, 35);
System.out.println(list);
// classe anonima a partire da una interfaccia
Collections.sort(list, (a, b) -> Integer.compare(a, b));
System.out.println(list);
Collections.sort(list, (a, b) -> Integer.compare(b, a));
System.out.println(list);
}
}
Utili quando si vuole isolare un sotto-comportamento in una classe a sé, senza dichiararne una nuova che si affianchi alla lista di quelle fornite dal package, ma stia “dentro” una classe più importante
danilo.pianini@unibo.itgianluca.aguzzi@unibo.itangelo.filaseta@unibo.itCompiled on: 2025-12-05 — versione stampabile