Closures II (Java)

In Java gibt es schon seit recht frühen Versionen die sogenannten inneren Klassen.

Davon gibt es einige Ausprägungen:

  • statische innere Klassen
  • nicht-statische innere Klassen
  • anonyme innere Klassen

Beispiel 1: statische innere Klasse:

public class MyOuterClass {
  private static class MyInnerClass {
    ...
  }
}

Beispiel 2: nicht-statische innere Klasse:

public class MyOuterClass {
  private class MyInnerClass {
    ...
  }
}

Beispiel 3: anonyme innere Klasse:

public class MyOuterClass {
  public Runnable myMethod(..) {
    return new Runnable() {
      public void run() {
        ...
      }
   }
}

Sind die statischen inneren Klassen nichts anderes als weitere Klassen, die halt nur in ihrer Sichtbarkeit speziell für die umgebende Klasse zugänglich sind, so sind die anderen beiden Ausprägungen viel weitgehender in die umgebende Klasse integriert. Alle Attribute und Methoden der umgebenden Klasse können auch von der inneren Klasse aus angesprochen werden. Es fehlt leider ein zum „super“ analoges Schlüsselwort „outer“, so dass man sich mit Konstrukten wie „MyOuterClass.this.method()“ behelfen muss, wenn man etwas ansprechen will, was sowohl in der inneren als auch in der umgebenden Klasse vorkommt.

Genauer betrachtet gilt das für die statischen inneren Klassen und die statischen Attribute und Methoden der umgebenden Klasse natürlich auch, wobei man hier einfach „MyOuterClass.myStaticMethod()“ aufrufen kann.

Um in den nicht-statischen und anonymen inneren Klasse auf die umgebenden Attribute und Methoden zugreifen zu können, muss also implizit eine Instanz der äußeren Klasse referenziert werden. Das sollte man beachten, weil diese Referenz natürlich gehalten wird und so eine innere Klasse verhindert, dass die umgebende Instanz der Garbagecollection zugeführt wird. Beim Serialisieren kann ein scheinbar kleines Objekt riesig werden, wenn es einer solchen inneren Klasse entstammt.

Praktisch sind diese inneren Klassen, wenn man mit einem Objekt zwei verschiedene Interfaces befriedigen will. Es kann auch so etwas banales sein wie ein Collection, die mit getImmutable() eine Variante von sich selbst herausgibt, wo alle Methoden, die zu Änderungen führen würden, UnsupportedOperationException werfen.

Aber wie kommt man damit zu einem Ersatz für die Closures?

Vielleicht so:

public interface Function {
  public Y f(X x);
}

und dann kann man so etwas machen wie

public class C {
  public Function createAdder(final int x) {
    return new Function() {
      public Integer f(Integer xx) {
        return x+xx;
      }
    };
  }
  
  
  public static void main(String args[]) {
    C c = new C();
    Function ff = c.createAdder(3);
    for (int i = 0; i < 3; i++) {       System.out.println("i=" + i + " s=" + ff.f(i));     }     System.out.println();     ff = c.createAdder(90);     for (int i = 0; i < 3; i++) {       System.out.println("i=" + i + " s=" + ff.f(i));     }   } }

Das erzeugt etwa diese Ausgabe:

i=0 s=3
i=1 s=4
i=2 s=5

i=0 s=90
i=1 s=91
i=2 s=92

Diese Quelltexte kann man auf Github finden.

Es kommt also dem nahe, was man an dieser Stelle mit Perl machen kann.

Mit den Lambda-Funktionen oder Closures aus Java 8 sieht es dann etwa so aus:

  public Function createAdder(final int x) {
    return (xx) -> x+xx;
  }

Offensichtlich hat man dadurch also eine kürzere Schreibweise gewonnen, was Konstrukte dieser Art viel attraktiver macht.

Share Button

Beteilige dich an der Unterhaltung

1 Kommentar

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

*