Montag, 29. Juli 2019

LaTeX: Einrücken von Absätzen verhindern

Im Deutschen eher unüblich ist das Einrücken der ersten Zeile eines Absatzes. Im Englischen ist dies normal und da LaTeX nun einmal aus den USA stammt, rückt es automatisch die erste Zeile ein. Das können Sie jedoch mit einem ganz einfachen Kommando verhindern.

\setlength{\parindent}{0em}

Einfach dieses Kommando an den Anfang des Dokuments einfügen und die Einrückung ist Geschichte.

Samstag, 27. Juli 2019

Variable Methoden-Parameter

In Java 5 wurden die variablen Methoden-Parameter eingeführt, d.h. man kann einer Methode eine variable Anzahl an Parametern übergeben. Das klingt seltsam, also sehen wir uns die Sache mal an einem Beispiel an. Sie kennen das folgende:


public void myMethod(int intParameter1, int intParameter2, String stringParameter) { 
    // Mach was schlaue mit den Parametern
}


Hier haben wir eine feste Parameteranzahl: Wir übergeben der Methode zwei int-Parameter und einen String-Parameter. Und das ohne jede Ausnahme. Diese drei Parameter müssen wir übergeben. Wir können der Methode aber keinen dritten int-Parameter geben. Wir müssen uns mit diesen beiden zufrieden geben. Sehen Sie sich mal das nächste Beispiel an.

public void myMethod(int [] intParameters) {
for (int myInt: intParameters) {
System.out.println(myInt);
}
}


Jetzt arbeite ich mit einem Array. Darin kann ich mehrere Parameter unterbringen und dieses Array übergebe ich an die Methode. Ich kann nun ein Array mit einem Parameterwert übergeben, aber ich kann auch eines mit zehn Parameterwerten übergeben. Das geht also schon mehr in die Richtung von variablen Parametern. Das nächste Beispiel zeigt die Neuerung in Java 5.

public void myMethod(int ... intParameters) {
for (int myInt: intParameters) {
System.out.println(myInt);
}
}


Die einzige Änderung zu dem Beispiel mit dem Array sind die drei Punkte zwischen Datentyp und Parametername. Damit markieren wir Varargs, also variable Parameter. Wir können beliebig viele int-Parameter übergeben. Intern werden diese Varargs wie ein Array behandelt, ohne dass wir uns beim Aufruf mit einem Array herumplagen müssen. Und damit wären wir bei einer wichtigen Frage: Wie rufe ich das Ding eigentlich auf?

public void start() {
myMethod(4);
myMethod(4,5);
myMethod(1,2,3,4,5,6,7,8,9,0);
}


Oben sehen Sie drei mögliche Aufrufe. Wir können die Methode also tatsächlich mit beliebig vielen int-Parametern aufrufen.

Es gibt allerdings eine wichtige Einschränkung: Nach einem Vararg dürfen keine weiteren Parameter mehr folgen. Etwas wie im folgenden Listing ist also nicht erlaubt und produziert einen Compiler-Fehler:

public void myMethod(int ... intParameters, String test) {
for (int myInt: intParameters) {
System.out.println(myInt);
}
}


Damit es funktioniert, müssen die nicht variablen Parameter vor dem Vararg stehen - also wie im nächsten Listing zu sehen:

public void myMethod(String test, int ... intParameters) {
for (int myInt: intParameters) {
System.out.println(myInt);
}
}

Die Annotation @FunctionalInterface

Die Annotation @FunctionalInterface erinnert vielleicht ein wenig an @Override - auch sie dient dazu, Hilfe vom Compiler zu bekommen, um Programmierfehler zu vermeiden. Um was geht es? Natürlich um funktionale Interfaces. Dazu an dieser Stelle ein ganz kleiner Exkurs, damit auch jeder die Sache versteht.

 Funktionale Interfaces wurden in Java 8 eingeführt. Sie bezeichnen Interfaces, die nur eine einzige abstrakte Methode definieren. Der folgende Code zeigt zwei Interfaces - das erste ist funktional, das zweite nicht.

 public interface FunctionInterface{
     void myMethod();
}

public interface NotFunctionalInterface{
    void myFirstMethod();
    void mySecondMethod();
}


 Interfaces mit nur einer Methode hat es doch schon immer gegeben. Warum benötigen sie jetzt auf einmal einen eigenen Namen? Weil es viel mehr ist als nur ein Name. In Java 8 haben funktionale Interfaces eine wirkliche syntaktische Bedeutung im Rahmen von Lambda-Ausdrücken. Diesem Thema widme ich bei Gelegenheit noch eigene Blog-Beiträge. Jetzt erst einmal zur Annotation.

Wir können funktionale Interfaces mit @FunctionalInterface markieren. Damit sagen wir dem Compiler: "He, Compiler. Dieses Interface soll funktional sein. Pass mal bitte mit auf." Jetzt sollte jeder von uns bis eins zählen können - das traue ich jedem Leser dieses Blogs zu. Warum benötigt man dann diese Annotation?

Seine wirkliche Stärke erhält @FunctionalInterface, wenn ein Interface geändert wird. Nehmen wir mal das erste Beispiel von oben. Ich führe in das Interface FunctionalInterface noch eine zweite Methode ein, weil sich mein Programm ja weiter entwickelt. Mit einem Schlag würden alle Lambda-Ausdrücke, die dieses Interface verwenden, nicht mehr funktionieren. Wenn ich mein funktionales Interface mit @FunctionalInterface markiere und ich füge irgendwann aus Versehen eine weitere Methode ein, wird der Compiler sich darüber beschweren und mich auf diesen Fehler aufmerksam machen. Im Folgenden noch ein Beispiel mit der Annotation.

public interface FunctionInterface{
    @FunctionalInterface
    void myMethod();
}

Die Annotation @Deprecated

Methoden können mit der Annotation @Deprecated als veraltet markiert werden, d.h. sie sollten nicht mehr benutzt werden. Auch Klassen, Interfaces usw. können mit dieser Annotation versehen werden. Sie können diese Information natürlich auch in den JavaDoc-Kommentar schreiben. Dort wird er mit @deprecated angegeben. Die Annotation @Deprecated hat jedoch den Vorteil, dass der Compiler diese Information nun kennt. Das hat zur Folge, dass Ihre IDE diese veralteten Elemente markieren kann. So streicht Eclipse diese Elemente aus und zeigt eine Warnung an. Zudem ist die Annotation immer verfügbar und damit auch diese sehr wichtige Information, auch wenn das JavaDoc nicht zur Verfügung steht.

Mehr gibt es zu @Deprecated nicht mehr zu sagen. Daher im Folgenden mal ein Beispiel:

public class MyClass {
    @Deprecated
    public void deprecatedMethod() {
        System.out.println("Ich bin veraltet!");
   }
}

@Deprecated
public class DeprecatedClass {
    @Deprecated
    public void deprecatedMethod() {
        System.out.println("Ich bin veraltet! Aber auch meine Klasse ist veraltet!");
   }
}

Die Annotation @Override

Die Annotation @Override dürfte die Annotation sein, die ich am häufigsten verwende, denn ich finde sie wahnsinnig nützlich. Leider kennen viele Programmierer sie nicht und deswegen möchte ich sie als erstes vorstellen.

Eine Studentengruppe kam einmal zu mir und bat mich um Hilfe, weil ihre toString()-Methode nicht funktionierte. Nicht ihre überschreibende Methode wurde aufgerufen, sondern die Methode toString() von Object, also diejenige, welche die Speicheradresse des Objekts ausgibt.

Der Fehler war relativ einfach: Sie hatten die Methode falsch benannt. Statt sie toString() zu nennen, hatten sie sich verschrieben und die Methode hatte den Namen tostring(). Ein kleiner Fehler mit großer Wirkung, denn die Laufzeitumgebung konnte nicht erkennen, dass hier eine Methode aus der Klasse Object überschrieben werden sollte. Und an dieser Stelle kommt @Override ins Spiel, denn hätten die Studenten diese Annotation benutzt, hätten sie nicht mehrere Stunden nach diesem Fehler gesucht.

Sehen Sie sich mal den folgenden Code an:

public class MySuperClass {
    public void method() {
        System.out.println("method() in MySuperClass");
   }
}

public class MySubClass {
    public void metod() {
        System.out.println("method() in MySubClass");
   }
}


Ich möchte gerne die Methode method() in der Unterklasse überschreiben. Aber wie in meinem oben beschriebenen Beispiel habe ich mich verschrieben. Der Compiler bemerkt das nicht und erzeugt zwei unterschiedliche Methoden. Somit bekomme ich wegen meines kleinen Schreibfehlers einen ziemlich doofen Laufzeitfehler, der unter Umständen schwer zu finden ist. Im folgenden Code habe ich die Annotation @Override eingebaut.

public class MySuperClass {
    public void method() {
        System.out.println("method() in MySuperClass");
   }
}

public class MySubClass {
  @Override
  public void metod() {
        System.out.println("method() in MySubClass");
   }
}


Mit dieser Annotation sage ich dem Compiler: "He, Compiler. Ich glaube, ich überschreibe da eine Methode. Aber pass mal lieber mit auf!" Und in meinem Beispiel wird der Compiler jetzt maulen und sagen, dass er die Methode metod() nicht überschreiben kann, weil es sie in der Oberklasse nicht gibt. Und schon habe ich aus einem potentiellen Laufzeitfehler einen Übersetzungszeitfehler gemacht. Und wenn der Compiler einen Fehler finden kann, ist das Gold wert, denn ich erhalte überhaupt kein lauffähiges Programm. Laufzeitfehler suchen kann erheblich mühsamer werden.

Das tut die Annotation @Override: Sie markiert eine Methode für den Compiler mit dem Hinweis, dass diese Methode überschrieben werden soll. Diese Annotation hat gleich mehrere Vorteile:


  1. Wie schon beschrieben erhalten wir einen Compilerfehler, wenn wir eine Methode nicht überschreiben. Damit sind Schreibfehler schon einmal ausgeschlossen. Zudem können wir den Compilerfehler auch nutzen, wenn wir uns unsicher sind, ob diese Methode wirklich in der Oberklasse existiert.
  2. Dokumentation: Programmierer dokumentieren ja normalerweise eher ungerne - eine Eigenart, die ihre Software nicht unbedingt besser macht. Die Information, ob eine Methode überschrieben wird, ist jedoch sehr wichtig und sollte dokumentiert werden. Wenn wir @Override verwenden, bekommen wir die Dokumentation also frei Haus mit geliefert.
  3. Wenn der Name der überschriebenen Methode in der Oberklasse ändert, bekommen das die Klassen, die Methoden überschreiben möchten, normalerweise nicht mit. Hier ist also Vorsicht und Umsicht von Seiten des Programmierers angesagt - darauf sollte man sich nicht verlassen. Mit @Override passt der Compiler mit uns zusammen auf. 
  4. Gute IDEs wie Eclipse oder NetBeans können die Annotation nutzen, um Sie als Programmierer zu unterstützen. Mal angenommen, Sie schreiben eine Klasse, die ein Interface implementieren soll. Das Interface existiert bereits, aber es enthält die Methodendeklarationen noch nicht. Dann implementieren Sie die Methode in der Klasse, klatschen @Override darüber und die IDE legt dann die Methode im Interface an. Das ist ziemlich praktisch.
Grundsätzlich sollten Sie @Override immer verwenden, wenn Sie eine Methode überschreiben oder eine abstrakte Methode implementieren. Sie sehen oben, dass die Annotation auch Vorteile mit sich bringt, wenn man sich ganz sicher ist, dass man sich beim Methodennamen nicht verschrieben hat. Ich persönlich verwende @Override auch bei toString() und equals(). Diese Annotation ist einfach, aber unglaublich praktisch. 

Freitag, 26. Juli 2019

Was sind Annotationen?

Annotationen wurden mit der Java-Version 5 eingeführt. Das liegt heute, 2019, gut 14 Jahre zurück, aber noch immer muss ich erkennen, dass nicht jeder die Annotationen kennt - oft nicht einmal die Standard-Annotationen. Dabei sind sie ein mächtiges Werkzeug. Ich möchte in den nächsten Blog-Beiträgen ein wenig in das Thema Annotationen eintauchen.

Annotationen sind Markierungen im Quellcode, also Meta-Informationen, Informationen über Informationen. In unserem Fall sind es Informationen über den Quelltext. Wir markieren bestimmte Teile des Quelltextes. Dann brauchen wir noch jemanden, der den Quelltext inspiziert, die Markierungen findet und darauf reagiert. Das kann der Compiler sein, ein Framework oder eine Java-Klasse, die wir selbst geschrieben haben.

Annotationen erkennt man an dem vorgestellten @-Zeichen. Im folgenden Listing sehen Sie eine Annotation (@Override), die ich im nächsten Beitrag genauer erläutern werde.

@Override
public void meineMethode() {
    System.out.println("Hallo Annotationen");
}


Diese Annotation wird vom Compiler ausgewertet und ist enorm nützlich. An dem Beispiel ist zu sehen, dass Annotationen an einer bestimmten Stelle stehen müssen. Ich hätte @Override nicht an eine Klasse oder ein Attribut schreiben dürfen. Das würde der Compiler mit einem Fehler quittieren.

In diesem ersten Beitrag wollte ich nur einige grundsätzliche Dinge über Annotationen schreiben. Es geht dann weiter mit der oben erwähnten Annotation @Override.

Ich stelle die folgenden Standard-Annotationen vor:

  1. @Override
  2. @Deprecated
  3. @FunctionalInterface
  4. @SupressWarnings
  5. @SafeVarargs