danilo.pianini@unibo.itgianluca.aguzzi@unibo.itangelo.filaseta@unibo.itCompiled on: 2025-12-05 — versione stampabile
extendsprotectedsuperfinal su classi e metodiÈ un meccanismo che consente di definire una nuova classe specializzandone una esistente, ossia
Lamp), realizzare un televisore (Tv)SimpleLamp), realizzare una lampadina con controllo livello di intensità luminosa (AdvancedLamp)Device diversi e specializzatiCounterpublic class Counter {
private int value;
public Counter(final int initialValue) {
this.value = initialValue;
}
public void increment() {
this.value++;
}
public int getValue() {
return this.value;
}
}
Counterpublic class UseCounter {
public static void main(String[] s) {
final Counter c = new Counter(0);
System.out.println(c.getValue()); // 0
c.increment();
c.increment();
System.out.println(c.getValue()); // 2
}
}
MultiCounterCounterCounter, offre un metodo multiIncrement(int)public class MultiCounter {
private int value;
public MultiCounter(final int initialValue) {
this.value = initialValue;
}
public void increment() {
this.value++;
}
public int getValue() {
return this.value;
}
/* Nuovo metodo */
public void multiIncrement(final int n) {
for (int i = 0; i < n; i++) {
this.increment();
}
}
}
MultiCounterpublic class UseMultiCounter {
public static void main(String[] s) {
final MultiCounter mc = new MultiCounter(10);
System.out.println(mc.getValue()); // 10
mc.increment();
mc.increment();
System.out.println(mc.getValue()); // 12
mc.multiIncrement(10);
System.out.println(mc.getValue()); // 22
}
}
MultiCounter2public class MultiCounter2 {
private Counter counter;
public MultiCounter2(final int initialValue) {
this.counter = new Counter(initialValue);
}
public void increment() {
this.counter.increment();
}
public int getValue() {
return this.counter.getValue();
}
/* Nuovo metodo */
public void multiIncrement(final int n) {
for (int i = 0; i < n; i++) {
this.counter.increment();
}
}
}
Counter (via delega) ma non abbiamo veramente “ridotto” la quantità di codice scrittoN punti, dovremo prima localizzare questi N punti e poi ivi applicare la modifica)class C extends D { ... }C eredita campi/metodi di D
C (la “visibilità” di un campo è ortogonale alla sua “presenza” in una classe)D superclasse, o classe base, o classe padreC sottoclasse, o classe figlia, o specializzazioneD (basta il codice binario)MultiCounter/* Si noti la clausola extends */
public class MultiCounter extends Counter {
/*
* I costruttori vanno ridefiniti. Devono tuttavia richiamare
* quelli ereditati dalla sopraclasse
*/
public MultiCounter(int initialValue) {
super(initialValue);
}
// increment e getValue automaticamente ereditati
// si aggiunge multiIncrement
public void multiIncrement(final int n) {
for (int i = 0; i < n; i++) {
this.increment();
}
}
}
MultiCounter come estensione di CountermultiIncrement()super, che chiama un costruttore (non privato) della classe padreUseMultiCounter continua a funzionare!MultiCounter è simile ad un oggetto di Counter
increment() e getValue()value (che in effetti è incrementato), anche se essendo privato è inaccessibile dal codice della classe MultiCounterCounter: metodo multiIncrement() e ridefinizione del costruttoreextends” (specializzazione)
protectedpublic e privateprotected?privateBiCounter – contatore bidirezionaledecrementcounterExtendibleCounter/* Il nome ExtendibleCounter è di comodo, più propriamente
andrebbe chiamata semplicemente Counter */
public class ExtendibleCounter {
/* campo value protetto */
protected int value;
public ExtendibleCounter(final int initialValue) {
this.value = initialValue;
}
public void increment() {
this.value++;
}
public int getValue() {
return this.value;
}
}
MultiCounterpublic class MultiCounter extends ExtendibleCounter {
public MultiCounter(final int initialValue) {
super(initialValue);
}
public void multiIncrement(final int n) {
// Ora realizzabile più efficientemente
if (n > 0) {
this.value = this.value + n;
}
}
}
BiCounterCounter (i.e. increment + getValue) senza un rendere accessibile in scrittura alle sottoclassi il campo value (direttamente o via setter)public class BiCounter extends ExtendibleCounter {
public BiCounter(final int initialValue) {
super(initialValue);
}
public void decrement() {
/* Ora this.counter è accessibile */
this.value--;
}
}
superincrement()LimitCounterpublic class LimitCounter extends ExtendibleCounter {
/* Aggiungo un campo, che tiene il limite */
protected final int limit;
public LimitCounter(final int limit) {
super(0);
this.limit = limit;
}
public boolean isOver() {
return this.getValue() == this.limit;
}
/* Overriding del metodo increment() */
public void increment() {
if (!this.isOver()) {
super.increment();
}
}
}
LimitCounterpublic class UseLimitCounter {
public static void main(String[] s) {
final LimitCounter c = new LimitCounter(5);
System.out.println(c.getValue()); // 0
System.out.println(c.isOver()); // false
c.increment();
c.increment();
System.out.println(c.getValue()); // 2
System.out.println(c.isOver()); // false
c.increment();
c.increment();
c.increment();
c.increment();
c.increment();
c.increment();
c.increment();
System.out.println(c.getValue()); // 5
System.out.println(c.isOver()); // true
}
}
LimitCounterLimitedLamp (via estensione) che contiene un contatore, e che ha un tempo di vita basato sul numero di accensioni ammesseEcoDomusController si compone di $n$ LimitedLamp, e ha la possibilità di verificare se tutte le lampadine sono esaurite, e di accendere la lampadina alla quale è rimasto più tempo di vitaEcoDomusController componesse $n$ SimpleLamp e $n$ LimitCounterLimitedLamp realizza alcuni metodi per delegazione al suo contatore
LimitCounterpublic class LimitCounter extends ExtendibleCounter {
private final int limit;
public LimitCounter(final int initialValue, final int limit) {
super(initialValue);
this.limit = limit;
}
public boolean isOver() {
return this.getDistanceToLimit() == 0;
}
public int getDistanceToLimit() {
return this.limit - this.value;
}
public void increment() {
if (!this.isOver()) {
super.increment();
}
}
}
SimpleLamppublic class SimpleLamp {
private boolean switchedOn;
public SimpleLamp() {
this.switchedOn = false;
}
public void switchOn() {
this.switchedOn = true;
}
public void switchOff() {
this.switchedOn = false;
}
public boolean isSwitchedOn() {
return this.switchedOn;
}
}
LimitedLamppublic class LimitedLamp extends SimpleLamp {
private LimitCounter counter;
public LimitedLamp(final int limit) {
super(); // Questa istruzione è opzionale
this.counter = new LimitCounter(0, limit);
}
public void switchOn() {
if (!this.isSwitchedOn()) {
// incremento solo se è una vera accensione
this.counter.increment();
}
if (!this.counter.isOver()) {
super.switchOn();
}
}
public int getRemainingLifeTime() { // delegazione a counter
return this.counter.getDistanceToLimit();
}
public boolean isOver() { // delegazione a counter
return this.counter.isOver();
}
}
EcoDomusControllerpublic class EcoDomusController {
/* Compongo n LimitedLamp */
final private LimitedLamp[] lamps;
public EcoDomusController(final int size, final int lampsLimit) {
this.lamps = new LimitedLamp[size];
for (int i = 0; i < size; i++) {
this.lamps[i] = new LimitedLamp(lampsLimit);
}
}
public LimitedLamp getLamp(final int position) {
return this.lamps[position];
}
private LimitedLamp toBeUsedNext() {
LimitedLamp best = null;
for (final LimitedLamp lamp : this.lamps) {
if (!lamp.isSwitchedOn() &&
( best == null ||
lamp.getRemainingLifeTime() > best.getRemainingLifeTime())) {
best = lamp;
}
}
return best;
}
/* Accendo una lampadina spenta, scegliendola in modo economico */
public void switchOnOne() {
final LimitedLamp lamp = this.toBeUsedNext();
if (lamp != null) {
lamp.switchOn();
}
}
/* Verifico se sono tutti accesi */
public boolean allOver() {
for (final LimitedLamp lamp : this.lamps) {
if (!lamp.isOver()) {
return false;
}
}
return true;
}
public String toString() {
String s = "";
for (final LimitedLamp lamp : this.lamps) {
s += (lamp.isSwitchedOn() ? "on" : "off");
s += "(" + lamp.getRemainingLifeTime() + ")" + " | ";
}
return s;
}
}
UseEcoDomusControllerpublic class UseEcoDomusController {
public static void main(String[] s) {
// Simulazione sessione di lavoro
final EcoDomusController controller;
controller = new EcoDomusController(5, 10);
System.out.println(controller);
// off(10) | off(10) | off(10) | off(10) | off(10) |
final LimitedLamp l = controller.getLamp(0);
l.switchOn();
l.switchOff();
l.switchOn();
System.out.println(controller);
// on(8) | off(10) | off(10) | off(10) | off(10) |
controller.switchOnOne();
controller.switchOnOne();
controller.switchOnOne();
controller.switchOnOne();
System.out.println(controller);
// on(8) | on(9) | on(9) | on(9) | on(9) |
}
}
class A { }
class B extends A { } // OK
B ha un costruttore di default, che invoca il costruttore senza argomenti (quello di default in questo caso) di Aclass A { A() { out.print("A"); } }
class B extends A { } // OK
B ha un costruttore di default, che invoca il costruttore senza argomenti di Aclass A { A(int x) { out.print("A" + x); } }
class B extends A { } // ERROR
B ha un costruttore di default, che vorrebbe invocare il costruttore di A senza argomenti, ma non lo trova! Il compilatore restituisce un errore.class A { }
class B extends A { B() { out.print("B"); } } // OK
// Stessa cosa di:
class B extends A { B() { super(); out.print("B"); } }
B ha un costruttore definito, che invoca implicitamente il costruttore senza argomenti (quello di default in questo caso) di Aclass A { A() { out.print("A"); } }
class B extends A { B() { out.print("B"); } } // OK
// Stessa cosa di:
class B extends A { B() { super(); out.print("B"); } }
B ha un costruttore definito, che invoca implicitamente/esplicitamente il costruttore senza argomenti di Aclass A { A(int x) { out.print("A" + x); } }
class B extends A { B() { out.print("B"); } } // ERROR
// Qua occorre fare:
class B extends A { B() { super(7); out.print("B"); } }
B ha un costruttore definito, che vorrebbe implicitamente invocare il costruttore di A senza argomenti, ma non lo trova! Il compilatore restituisce un errore.super), altrimenti il costruttore di default verrà chiamato, se c’ènewclass A {
protected int i;
public A(int i) {
System.out.println("A().. prima " + this.i);
this.i = i;
System.out.println("A().. dopo " + this.i);
}
}
class B extends A {
protected String s;
public B(String s, int i) {
super(i);
System.out.println("B().. prima " + this.s + " " + this.i);
this.s = s;
System.out.println("B().. dopo " + this.s + " " + this.i);
}
public static void main(String[] s) {
B b = new B("prova", 5); // Cosa succede?
}
}
super)superC può includere una invocazione del tipo super.m(..args..)m, ossia viene eseguito il metodo m della superclasse
n (su this), allora si ritorna a considerare la versione più specifica a partire dalla classe di partenza Cclass C {
protected int i;
void m() {
System.out.println("C.m.. prima " + i);
this.i++;
System.out.println("C.m.. dopo " + i);
}
}
class D extends C {
D(int i) {
this.i = i;
}
void m() {
super.m();
System.out.println("D.m.. dopo " + this.i);
}
public static void main(String[] s) {
new D(5).m(); // Cosa succede?
}
}
class E {
protected int i;
void m() {
this.i++;
this.n();
}
void n() {
this.i = this.i + 10;
}
}
class F extends E {
void n() {
this.i = this.i + 100;
}
public static void main(String[] s) {
F f = new F();
f.i = 10;
f.m();
System.out.println("" + f.i);
}
}
LimitCounterpublic class LimitCounter extends ExtendibleCounter {
private final int limit;
public LimitCounter(final int initialValue, final int limit) {
super(initialValue);
this.limit = limit;
}
public boolean isOver() {
return this.getDistanceToLimit() == 0;
}
public int getDistanceToLimit() {
return this.limit - this.value;
}
public void increment() {
if (!this.isOver()) {
super.increment();
}
}
}
increment() su un UnlimitedCounter?LimitCounterLimitCounter si chiama this.isOver() che chiama this.getDistanceToLimit()this.getDistanceToLimit() eseguita è quella di UnlimitedCounterpublic class UnlimitedCounter extends LimitCounter {
public UnlimitedCounter() {
super(0, Integer.MAX_VALUE);
}
public int getDistanceToLimit() {
// Quindi il contatore non scade mai
return Integer.MAX_VALUE;
}
}
UnlimitedCounterpublic class UseUnlimitedCounter {
public static void main(String[] s) {
final UnlimitedCounter uc = new UnlimitedCounter();
System.out.println("isOver: " + uc.isOver()); // false
System.out.println("LifeTime: " + uc.getDistanceToLimit());
uc.increment();
uc.increment();
uc.increment();
System.out.println("isOver: " + uc.isOver()); // false
System.out.println("LifeTime: " + uc.getDistanceToLimit());
}
}
C ne ha una, ed è accessibile ai suoi oggettiC, associa il codice corrispondente da eseguire, ossia la classe che riporta il bodythis. e super.Come sono fatte le tabelle relative alle classi LimitedCounter e UnlimitedCounter nell’esempio precedente?

finalsuper è possibile prendere classi esistenti e modificarle con grande flessibilitàfinalfinal anche metodi e intere classifinal è un metodo che NON può essere ri-definito per overridingfinal non può essere estesafinal, ad esempio Stringprotected a public)public a protected, o da public a private)final$\Rightarrow$ sono tutte conseguenze del principio di sostituibilità
Objectjava.lang.ObjectObjectObject è la radice della gerarchia di ereditarietà di JavaObjectFornisce alcuni metodi di utilità generale
toString(), che stampa informazioni sulla classe e la posizione in memoria dell’oggettoclone(), per clonare un oggettoequals() e hashCode(), usati nelle collectionnotify() e wait(), usati nella gestione dei threadequals è definito nella classe Object e può essere sovrascrittoequals è usato per confrontare due oggetti==equals per definire un confronto semanticoclass Person {
private String name;
private int age;
// ...
@Override
public boolean equals(Object o) {
return age == person.age && Objects.equals(name, person.name);
}
}
@Override@Override
class Person {
// ...
@Override
public String toString() {
// ...
}
}
danilo.pianini@unibo.itgianluca.aguzzi@unibo.itangelo.filaseta@unibo.itCompiled on: 2025-12-05 — versione stampabile