Freitag, 3. September 2010

Playing with classes

In der Programmiersprache Java können Klassen in Klassen und Schnittstellen in Schnittstellen deklariert werden. Java unterscheidet deshalb Top-Level und verschachtelte Klassen. Verschachtelte Klassen können statisch sein. Top-Level Klassen hingegen nicht.Verschachtelte Klassen dienen zur Kapselung von Informationen (IHP), verletzen aber manchmal das Gebot lesbaren Quellcode zu schreiben. Das Ausnutzen der Syntax von inneren und anonymen Klassen fördert häufig nicht die Verständlichkeit eines Quellcodes.

Klassen können nicht nur in Klassen, sondern auch in Methoden deklariert werden. Anonyme Klassen sind als Methodenargument verwendbar und  können bei der Deklaration sogar Methoden überschreiben. Anonyme Klassen implementieren eine Schnittstelle oder erben von einer einzigen Klasse. Gleichzeitig eine Schnittstelle zu implementieren und von einer Klasse zu erben ist nicht möglich. Anonyme Klassen trifft man bei Callback-Implementierungen und in speziellen Technologiefeldern an. Ein typisches Beispiel für ein solches Technologiefeld ist die Eclipse RCP Programmierung.

Verschachtelte und anonyme Klassen unterliegen den Bedingungen, die Java durch seine Syntax vorgibt. Auf syntaxspezifische Details soll in dem vorliegenden Blogbeitrag nicht näher eingegangen werden. Nachfolgend eine kleine Galerie von verschachtelten und anonymen Klassen an deren Syntax man sich erst gewöhnen muss.

Innere Klasse und anonyme innere Klasse

import static org.junit.Assert.*;
import org.junit.Test;

public class AnonymousInnerClass {

    private static final String OUTPUT = "test-output";

    private class InnerClass {

        public String getOutput() {

            return OUTPUT;
        }
    }

    @Test
    public void testInnerClass() {

        AnonymousInnerClass.InnerClass innerClass = new AnonymousInnerClass().new InnerClass();

        final String result = innerClass.getOutput();

        assertEquals(OUTPUT, result);
    }

    @Test
    public void testAnonymousInnerClass() {

        final String result = new InnerClass() {

            public String getOutput() {

                return OUTPUT;
            }

        }.getOutput();

        assertEquals(OUTPUT, result);
    }
}

Anonyme abstrakte statische innere Klasse

import static org.junit.Assert.*;
import org.junit.Test;

public class AnonymousAbstractStaticInnerClass {

    private static final String OUTPUT = "test-output";
  
    private abstract static class StaticInnerClass {
         
        public String getOutput() {
         
            return OUTPUT;
        }
    }
 
    @Test
    public void testAnonymousAbstractStaticInnerClass() {
     
        AnonymousAbstractStaticInnerClass.StaticInnerClass staticInnerClass =
                                                         new AnonymousAbstractStaticInnerClass.StaticInnerClass() {};
     
        final String result = staticInnerClass.getOutput();
      
        assertEquals(OUTPUT, result);
    }
}

Anonyme abstrakte methoden-lokale innere Klasse

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class AnonymousAbstractMethodLocalInnerClass {

    private static final String OUTPUT = "test-output";
  
    @Test
    public void testMethodLocalInnerClass() {
      
        abstract class AbstractMethodLocalInnerClass {
          
            public String getOutput() {
              
                return OUTPUT;
            }
        }
      
        final String result = new AbstractMethodLocalInnerClass() {
          
            public String getOutput() {
              
                return OUTPUT;
            }
          
        }.getOutput();
      
        assertEquals(OUTPUT, result);
    }
}

Anonyme Klasse auf einer Schnittstelle basierend als Methodenparameter

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class AnonymousClassAsMethodParameter {

    private static final String OUTPUT = "test-output";
   
    private interface Interface {

        public String getOutput();
    }

    public String methodWithAnonymousClassParameter(Interface interfaceArg) {

        return interfaceArg.getOutput();
    }

    @Test
    public void testAnonymousClassAsMethodParameter() {

        final String result = methodWithAnonymousClassParameter(new Interface() {

            public String getOutput() {

                return OUTPUT;
            }
        });

        assertEquals(OUTPUT, result);
    }
}

Vielfältige andere Kombinationsmöglichkeiten von verschachtelten und anonymen Klassen sind denkbar und bleiben dem Leser des Blogbeitrages als Spielmöglichkeit offen. Dank dem JUnit-Plugin in Eclipse macht gerade das Schreiben von kleinen Testroutinen sehr viel Spass!


Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.