Nicht-instantiierbare Klassen

EDIT: Off-Topic abgetrennt von http://forum.byte-welt.net/threads/11780-Innen-generisch-außen-nicht : Es ging um einen Blog-Beitrag

BTW, bezüglich des Kommentars im Blog

Übrigens: Wer solche seltsamen Konstruktoraufrufe wie im Beispielcode noch nicht gesehen hat, ist nicht allein – ich kannte die Syntax auch nicht…

Nachdem ich mal gesehen habe, dass jemand eine handvoll static Utility-Methods, die ich geschrieben hatte, mit new ContainingClass().staticMethod() aufrief und static inner classes mit new Outer().new Inner() instantiiert hat, bin ich mit /* Private constructor to prevent instantiation */ seeehr gewissenhaft :wink: (auch wenn gerade das in diesem Fall ja nicht gewünscht ist)

OT

Deswegen nutze ich für Klassen, von denen es keine Instanz geben soll, z.B. weil sie nur nur statische Hilfsmethoden oder Konstanten enthalten, eine Enum ohne Elemente. Dann kann man sich den privaten Konstruktor sparen. Im anderen Forum fand ich auf den Einwand „Enums ohne Elemente sind komisch“ eine sehr passende Antwort: „Mengenlehre macht ohne die leere Menge auch keinen Sinn.“

Naja…

Die leere Menge wäre im Sinne von Klassen eine Klasse ohne Methoden und Felder. Es ist und bleibt ein Hack.

Auch wenn Enums deutlich mehr mit Mengen zu tun haben als allgemeine Klassen, stimme ich @schlingel zu. Im originären Sinne macht es nur Sinn Enums zu definieren, die mindestens einen Wert haben (und selbst dann nur im Sinne der Erweiterbarkeit).
Allerdings könnte man auch sagen, dass ein leerer, privater Konstruktor auch ein Hack ist. Ein Konstruktor ist schließlich dafür gedacht, dass er beim Erzeugen eines Objektes etwas macht. Dass man einen leeren Konstruktor mit privater Sichtbarkeit verwenden kann, um die Objekterzeugung zu verhindern ist eher ein Seiteneffekt. Daher sieht man häufig in Code in welchem das gemacht wird auch einen Kommentar à la “prevent constructing”.
Java hat schlichtweg kein natives Sprachmittel um zu sagen “diese Klasse soll nicht instantiiert werden”.

Bombig. Dafür hat man ein frei stehendes Semikolon, welches bei Neulingen (und nicht nur bei denen) für Verwirrung sorgt („Toll, ein überflüssiges Semikolon. Oh, warum kompiliert das denn nicht mehr?“). Eine Zeile gespart, Verwirrung gestiftet. Keine gute Rechnung. Von der Syntax abgesehen, geht auch noch jede Semantik flöten.

Im anderen Forum fand ich auf den Einwand „Enums ohne Elemente sind komisch“ eine sehr passende Antwort: „Mengenlehre macht ohne die leere Menge auch keinen Sinn.“

Ja, man sieht schon am Marker-Interface, wie schön das ist.

Es geht um Lesbarkeit, Konsistenz und Konventionen. Nicht darum, irgendwelchen wissenschaftliche Triebe zu befriedigen. Sowas kommt üblicherweise auch von Leuten, die mit irgendwelchen Abstraktionen hochkomplexe Einzeiler schaffen, die bei jeder Änderung eh wieder umgebaut werden müssen… (jaja, Vorurteil… wer nicht mag, muss sich ja nicht angesprochen fühlen…)

Bei der Enum-Lösung würde mich am ehesten die „Semantik“ daran stören. Also, das ist ein Sprachmittel, das nicht dazu gedacht war, nicht-instantiierbare Klassen zu modellieren. Und im Gegensatz dazu kümmert sich ein Konstruktor um die Instantiierbarkeit, und das „private“ um das „nicht“ :smiley: Ich find’s klarer, aber es ist vielleicht nur ein Detail.

Wenn die Klasse nicht instanziierbar sein soll könnte man sie doch auch abstract deklarieren, oder ist das zu einfach…?

bye
TT

Da abstrakte Klassen nicht final sein können (das kann nur Chuck Norris), ist das kein zuverlässiges Mittel. Jemand könnte auf die Idee kommen, die Klasse zu extenden und dann zu instanzieren.

Wenn’s nur die eine Zeile wäre. Checkstyle will dann noch einen Javadoc-Kommentar (ich weiß, kann man anders konfigurieren, aber im Team habe ich auf die Checkstyle-Konfiguration keinen Einfluss) und das Coverage Tool meckert, dass meine Tests den Konstruktor nie aufrufen. Muss ich also noch was konfigurieren, damit das nicht angemeckert wird und meine Coverage nicht leidet.

Und zu den Einwänden der Vorredner „Semantik […]“, „Enums sind nicht dazu gedacht“ etc.: Da finde ich den Begriff von Enums viel zu eng gefasst. Enums können (sogar abstrakte) Methoden haben, Interfaces implementieren, man kann damit Singletons bauen (steht sogar bei Effective Java). Sie sind also von vorn herein als sehr viel mehr konzipiert als nur eine definierte Menge von benamten Elementen. Ich sehe sie darum mehr als gepimpte (oder intelligent beschnittene) Klassen an. Und halte alles, was man mit Klassen macht auch bei Enums für angemessen. Außerdem, was würde man beim Lesen einer leeren Enum denken? Richtig: „Es gibt keine Instanzen“. Ich finde, noch deutlicher kann man die Intention semantisch nicht machen. Aber da kommen wir wohl nicht zusammen…

[QUOTE=nillehammer]Wenn’s nur die eine Zeile wäre. Checkstyle will dann noch einen Javadoc-Kommentar (ich weiß, kann man anders konfigurieren, aber im Team habe ich auf die Checkstyle-Konfiguration keinen Einfluss) und das Coverage Tool meckert, dass meine Tests den Konstruktor nie aufrufen. Muss ich also noch was konfigurieren, damit das nicht angemeckert wird und meine Coverage nicht leidet.

Und zu den Einwänden der Vorredner “Semantik […]”, “Enums sind nicht dazu gedacht” etc.: Da finde ich den Begriff von Enums viel zu eng gefasst. Enums können (sogar abstrakte) Methoden haben, Interfaces implementieren, man kann damit Singletons bauen (steht sogar bei Effective Java). Sie sind also von vorn herein als sehr viel mehr konzipiert als nur eine definierte Menge von benamten Elementen. Ich sehe sie darum mehr als gepimpte (oder intelligent beschnittene) Klassen an. Und halte alles, was man mit Klassen macht auch bei Enums für angemessen. Außerdem, was würde man beim Lesen einer leeren Enum denken? Richtig: “Es gibt keine Instanzen”. Ich finde, noch deutlicher kann man die Intention semantisch nicht machen. Aber da kommen wir wohl nicht zusammen…[/QUOTE]
Ich glaube, da kommst Du mit vielen Entwicklern nicht zusammen. Das Konstrukt gehört mMn überhaupt nicht zur Standardnutzung von Enums. Ich kann verstehen, dass Checkstyle und Coverage nerven, aber das Problem haben sicherlich auch andere im Projekt. Also mit denen reden und das Problem anpassen.

Eine Lösung bei der langjährige Entwickler erst einmal mit der Augenbraue zucken finde ich immer bedenklich.

Bei einem Enum denke ich eigentlich nicht an “es gibt keine Instanzen”. Ich denke an “oh, hier gibt es mehr Möglichkeiten als nur Ja und Nein”. Jetzt würde ich auf jeden Fall nicht erwarten, dass sich das Enum wie eine nicht instanziierbare Klasse verhält. Ich würde eigentlich noch nicht mehr auf die Idee kommen, dort genauer hineinzuschauen. Und wenn ich dann die “spezielle Nutzung” eines solchen Enums irgendwo sehen würde, würde ich das für einen Fehler halten.

[quote=nillehammer]Jemand könnte auf die Idee kommen, die Klasse zu extenden und dann zu instanzieren.[/quote]Vielleicht bin ich da zu naiv aber:

na und? solle er doch…

Statische Methoden sind nocht überschreibbar. Wo ist also das Risiko?

bye
TT

Die Frage ist doch, was ist jedem von euch bei der Programmierung wichtig?

a) Möglichst viel Code einzusparen? Dann nutzt Enums für Nicht-instaniierbare Klassen und nutzt sonstige kryptische Konstrukte, die kurz und mächtig sind, dafür aber wenig schreibarbeit benötigen

b) Möglichst verständlichen Code schreiben? Dann sind wohl private Konstruktoren und Enums für Aufzählungen an der Reihe.

Ich denke dies ist ein ewiger Streit zwischen den Künstlern und Pragmatikern unter uns. Ich persönlich zähle mich eher zu zweiteren.

[quote=Timothy_Truckle;90988]Vielleicht bin ich da zu naiv aber:

na und? solle er doch…

Statische Methoden sind nocht überschreibbar. Wo ist also das Risiko?[/quote]Jemandem die Möglichkeit zu geben mit deinem Code dumme und nutzlose Dinge anzustellen ist selten eine gute Idee. Eine wirklich gute API oder Bibliothek schütz auch den Anwender davor, sich selbst zu verwirren. :wink:

Ich habe nicht den ausgangsbeitrag gelesen, aber warum macht man nicht eine privaten konstruktor in einer final klasse ?!

Fuer mich geht es darum, was erwartet mein Gegenueber wenn er eine Klasse in die Hand bekommt. Bei einem Enum ist die Erwartung eine bestimmte, bei einer abstrakten Klasse ebenso. Keine der beiden ist fuer mich als ein logisches “das sollte nicht instanziiert werden”.

Natuerlich kann man zb mit enums das selbe erreichen, aber man sollte sich denken, wie andere, was andere, die diese Klasse nutzen als Erwartungshaltung haben wenn sie dies sehen. Da ist meiner ansicht nach es auch nicht hilfreich zu sagen “ja ich sehe die Def. als zu eng” - das mag akzeptabel sein, solange man fuer sich arbeitet, ansonsten scheint es mir wie “ein geisterfahrer auf der A1” - “Einer ??? tausende !”

[quote=Natac]Jemandem die Möglichkeit zu geben mit deinem Code dumme und nutzlose Dinge anzustellen ist selten eine gute Idee.[/quote]Diese “Bevormunding” auf Kosten der Lesbarkeit einzubauen aber auch…

bye
TT

Wenn ich das richtig verstanden habe:

a) Man muss aufgrund bestehender Checkstyle-Regeln Javadoc angeben, was nervt
b) Test-Coversage ärgert einen, weil der Konstruktor nicht aufgerufen wird

Aber ich finde, das könnte man beheben oder diesen Tod halt sterben. :slight_smile:

*** Edit ***

[QUOTE=Timothy_Truckle;90992]Diese „Bevormunding“ auf Kosten der Lesbarkeit einzubauen aber auch…
[/QUOTE]
Das finde ich sogar noch schlimmer. Lesbarkeit > all. Bei gutem Code geht es doch immer nur um die Wartbarkeit. Müsste ich den Code nicht anpassen, könnte er so schlecht sein, wie möglich. Wenn ich aber Änderungen vornehmen muss, muss ich zunächst den Code verstehen.

[QUOTE=Sym]Wenn ich das richtig verstanden habe:

a) Man muss aufgrund bestehender Checkstyle-Regeln Javadoc angeben, was nervt[/quote]
Checkstyle hilft nicht beim Verwender die Lesbarkeit zu verbessern.

[QUOTE=Sym;90993]
b) Test-Coversage ärgert einen, weil der Konstruktor nicht aufgerufen wird[/quote]
aergert einen weil man nicht 100% erreicht ? das pure Erreichen einer hohen Testcoverage ist ja auch nicht das ziel.

Ich finde die absolute Aussage " > all" zu riskant. Lesbarkeit ist wichtig und muss immer im Auge betrachtet werden. Doch alles auf diese zu setzen halte ich fuer nicht ratsam. Bei einem guten Programmierer kommt sie so und so frei mit dazu :slight_smile:

In der Regel sehe ich das schon so. Natürlich sollte muss die Regel auch mal gebrochen werden - zum Beispiel, wenn es um performanzkritische Dinge geht. Aber dann zählt für mich eine gute “Warum”-Dokumentation zu der Lesbarkeit.

Wenn ich Code anpassen (oder warten) möchte, muss ich ihn verstehen. Nur verständlicher Code ist guter Code. Unverständliche Codestellen müssen gekapselt und dokumentiert sein. Damit ist die Lesbarkeit wieder gewährleistet.

Das finde ich einen wichtigen Hinweis. Da gibt es ja sogar einen best Practice für: „Vermeide Überraschungen.“ Ich denke nur, dass z.B. anfangs auch niemand erwartet hat, eine Enum, welche evtl. ein Interface implementiert und ggf. einiges an Funktionalität bietet, als beste Möglichkeit einer Singleton-Implementierung zu sehen. Da mögen lanjährige Programmierer, welche das sonst mit static-Feldern gemacht haben, auch zunächst die Augenbrauen hochgezogen haben. Heute ist das ein etabliertes Pattern. Ich finde nach wie vor den „Überraschungseffekt“ einer Enum ohne Elemente nicht so hoch, dass das die Vorteile aufwiegt. Aber ich will mich hier auch nicht zum Avangardisten aufschwingen. Die Leidenschaft, mit der hier argumentiert wurde, zeigt ja ganz deutlich, dass die Erwartungshaltung allgemein eine andere ist.

also wer das verwendet (für Instanziierungskontrolle), kann von dieser Enum-Lösung hier (für Instanziierungskontrolle) doch nicht mehr überrascht werden,

ich vermute das ist kein Gegenbeispiel, sondern gehört zusammen: beides ok, oder beides als komisch abgelehnt,
ich selber bin irgendwie meinungsunentschlossen

(edit: erst ein Suchmaschinen-Treffer zu ‚Instanziierungskontrolle‘ bisher weltweit, das kann ja was werden nun :wink: ,
‚meinungsunentschlossen‘ auch knapp gesät )

Wo ist das Pattern etabliert? Weil es irgendwo in einem Best-Practise steht? Ich habe so etwas noch nicht im Code gesehen.

Es passt auch gar nicht zu Oracles Definition, wie ich finde

An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it. Common examples include compass directions (values of NORTH, SOUTH, EAST, and WEST) and the days of the week.

Naja, man kennt es schon. Ich hab’ dieses Semester Markus Struberg als Vortragenden in einer Lehrveranstaltung. (Entwickler CDI, OpenWebBeans, etc.) Der hat dieses Pattern auch kurz erwähnt als es um Singletons-Implementierungen ging.

Diese ganze Lösungen haben alle Vor- und Nachteile die über Stilfragen hinausgehen. Da sie sich auch puncto ClassLoading/Veränderbarkeit anders verhalten können.

Irgendwer hat’s eh schon erwähnt. Java stellt hier einfach keine probate Mittel zur Verfügung das zu implementieren ohne dass sich hier Kritikpunkte ergeben würden.