Clase anonime

Dănuț Chindriș · April 7, 2020

În designul orientat pe obiecte, există situații în care avem nevoie de o implementare specializată a unei clase, despre care știm că nu va fi folosită în niciun alt loc. În astfel de ocazii, avem la îndemână clasele anonime.

Situația 1

Subclasă anonimă a unei clase specificate

Să presupunem că avem o clasă Food definită astfel:

class Food implements Eatable {

  @Override
  public String serve() {
      return "food";
  }
}

Dacă avem nevoie de o subclasă a acesteia, în mod normal o extindem într-o nouă clasă, folosind cuvântul extends. Totuși, în cazul în care avem nevoie de o subclasă despre care știm că nu va mai fi utilă și în alte locuri, putem crea o implementare anonimă, astfel:

// create an instance of an anonymous subclass of Food
final Food food = new Food() {

  @Override
  public String serve() {
      return "new food";
  }
};

Atunci când apelăm food.serve(), valoarea returnată va fi "new food", datorită polimorfismului dinamic. Într-adevăr, tipul variabilei food esteFood, dar obiectul pe care îl referențiază este o instanță a clasei anonime ce extinde Food. Aici, cuvântul extends este implicit, dar noi știm că această clasă anonimă extinde clasa Food. Deci, instanța clasei anonime, creată in-place, are o relație de tipul is-a cu Food.

Scopul unei astfel de clase anonime este de a suprascrie (override) una sau mai multe dintre metodele superclasei. În cazul nostru, clasa anonimă suprascrie metoda serve().

Situația 2

Clasă ce implementează în mod anonim o interfață specificată

Similar primului caz, putem crea o clasă anonimă care implementează o interfață in-place. De exemplu, având interfața:

interface Eatable {

  String serve();
}

putem crea o implementare anonimă a acesteia, astfel:

final Eatable eatable = new Eatable() {

  @Override
  public String serve() {
      return "some food";
  }
};

La prima vedere, poate părea ciudat să folosim cuvântul new împreună cu numele unei interfețe - new Eatable(). Mai mult decât atât, vedem că am aplicat paranteze rotunde numelui interfeței - Eatable() -, ca și când am apelat un constructor.

Totuși, exprimarea este validă pentru că, în continuare, am furnizat implementarea anonimă a interfeței. Am făcut lucrul acesta între acolade. Aici observăm caracterul punct-și-virgulă după acolada de sfârșit, pentru că este sfârșitul statement-ului început o dată cu declarația variabilei eatable - în Java fiecare statement trebuie terminat cu punct-și-virgulă.

Așadar, aici cuvântul implements este implicit, dar știm că e vorba despre o implementare anonimă a interfeței Eatable. Deci, cuvantul new îl aplicăm constructorului clasei anonime - constructor apelat prin () - prezente dupa Eatable, care este numele interfeței pe care o implementăm aici.

O observație ar fi că o clasă anonimă definită astfel nu poate implementa decât o interfață, cea numită în definiția clasei. Acest lucru e restrictiv, pentru că o clasă definită în mod obișnuit - class MyClass {...} - poate implementa mai multe interfețe.

Situația 3

Clasă internă anonimă, definită pe locul unui argument

De multe ori nu avem nevoie de o variabilă care să referențieze o instanță de clasă anonimă. Așa că definim clasa și o instanțiem pe loc, acolo unde vrem s-o transmitem ca parametru unei metode. De exemplu, implementăm ad-hoc interfața Comparator, instanțiem clasa anonimă și transmitem ca parametru această instanță metodei sort(), astfel:

Collections.sort(Arrays.asList("ghi", "abc", "def"), new Comparator<String>() {

  @Override
  public int compare(String a, String b) {
      return a.compareTo(b);
  }
});

Twitter, Facebook