Entspricht #define in Java?

8

Ich schreibe eine Bibliothek, die Code enthalten muss, wenn eine bestimmte Bibliothek enthalten ist. Da dieser Code überall im Projekt verstreut ist, wäre es schön, wenn Benutzer nicht alles selbst kommentieren / auskommentieren müssten.

In C wäre das einfach genug mit einem #define in einer Kopfzeile und dann Codeblöcken, die mit #ifdefs umgeben sind. Natürlich hat Java nicht den C-Präprozessor ...

Zur Klarstellung - mehrere externe Bibliotheken werden mit mir verteilt. Ich möchte sie nicht alle einbeziehen müssen, um meine ausführbare Dateigröße zu minimieren. Wenn ein Entwickler eine Bibliothek enthält, muss ich in der Lage sein, sie zu verwenden, und wenn nicht, kann sie einfach ignoriert werden.

Was ist der beste Weg, dies in Java zu tun?

    
Justin 21.12.2009, 22:21
quelle

12 Antworten

6

Wie bereits erwähnt, gibt es in Java kein # define / # ifdef. Aber bezüglich Ihres Problems, optionale externe Bibliotheken zu haben, die Sie verwenden würden, falls vorhanden, und nicht zu verwenden, wenn nicht, könnte die Verwendung von Proxy-Klassen eine Option sein (wenn die Bibliotheksschnittstellen nicht zu groß sind).

Ich musste dies einmal für die Mac OS X spezifischen Erweiterungen für AWT / Swing (in com.apple.eawt. *) tun. Die Klassen befinden sich natürlich nur auf dem Klassenpfad, wenn die Anwendung unter Mac OS läuft. Um sie nutzen zu können und trotzdem die gleiche App auf anderen Plattformen zu verwenden, schrieb ich einfache Proxy-Klassen, die einfach die gleichen Methoden wie die ursprünglichen EAWT-Klassen anbieten. Intern verwendeten die Proxies einige Überlegungen, um festzustellen, ob die realen Klassen im Klassenpfad waren und alle Methodenaufrufe durchlaufen würden. Indem Sie die java.lang.reflect.Proxy Klasse verwenden, Sie können sogar Objekte eines in der externen Bibliothek definierten Typs erstellen und weitergeben, ohne sie zur Kompilierungszeit verfügbar zu haben.

Der Proxy für com.apple.eawt.ApplicationListener sah beispielsweise so aus:

%Vor%

All dies macht nur Sinn, wenn Sie nur wenige Klassen aus einer externen Bibliothek benötigen, weil Sie zur Laufzeit alles über Reflektion tun müssen. Bei größeren Bibliotheken benötigen Sie wahrscheinlich eine Möglichkeit, die Generierung der Proxies zu automatisieren. Wenn Sie jedoch wirklich auf eine große externe Bibliothek angewiesen sind, sollten Sie sie nur zur Kompilierzeit benötigen.

Kommentar von Peter Lawrey: (Entschuldigung zu bearbeiten, es ist sehr schwer, den Code in einen Kommentar zu setzen)

Das folgende Beispiel ist generisch nach Methode, so dass Sie nicht alle beteiligten Methoden kennen müssen. Sie können dies auch generisch nach Klasse machen, sodass Sie nur eine InvocationHandler-Klasse benötigen, die so codiert ist, dass sie alle Fälle abdeckt.

%Vor%     
Simon Lehmann 21.12.2009, 22:59
quelle
11

Es gibt keine Möglichkeit, innerhalb von Java das zu tun, was Sie wollen. Sie könnten die Java-Quelldateien vorverarbeiten, aber das liegt außerhalb des Java-Bereichs.

Können Sie die Unterschiede nicht abstrahieren und dann die Implementierung variieren?

Aufgrund Ihrer Klarstellung klingt es so, als könnten Sie eine Factory-Methode erstellen, die entweder ein Objekt aus einer der externen Bibliotheken oder eine "Stub" -Klasse zurückgibt, deren Funktionen das tun, was Sie in der " nicht verfügbarer "bedingter Code".

    
Steve Emmerson 21.12.2009 22:25
quelle
5

In Java könnte man verschiedene Ansätze verwenden, um dasselbe Ergebnis zu erzielen:

Der Java-Weg besteht darin, ein variables Verhalten in eine Reihe separater Klassen einzubinden, die über eine Schnittstelle abstrahiert werden, und dann die erforderliche Klasse zur Laufzeit zu verbinden. Siehe auch:

Vlad Gudim 21.12.2009 22:38
quelle
4

Nun, Java-Syntax ist nahe genug an C, dass Sie einfach den C-Präprozessor verwenden könnten, der normalerweise als separate ausführbare Datei ausgeliefert wird.

Aber Java geht nicht wirklich darum, Dinge zur Kompilierzeit zu machen. Die Art und Weise, wie ich ähnliche Situationen zuvor behandelt habe, ist Reflexion. In Ihrem Fall würde ich, da Ihre Aufrufe der möglicherweise nicht vorhandenen Bibliothek im Code verstreut sind, eine Wrapper-Klasse erstellen, alle Aufrufe der Bibliothek durch Aufrufe der Wrapper-Klasse ersetzen und dann die Reflektion in der Wrapper-Klasse verwenden in der Bibliothek aufzurufen, wenn sie vorhanden ist.

    
JaakkoK 21.12.2009 22:49
quelle
2

Verwenden Sie eine Konstante :

  

Diese Woche erstellen wir einige Konstanten   das haben alle Vorteile der Verwendung   die Einrichtungen des C-Präprozessors zu   definieren Kompilierzeitkonstanten und   bedingt kompilierter Code.

     

Java hat das Ganze losgeworden   Vorstellung eines textuellen Präprozessors (if   Du nimmst Java als "Nachkomme" von   C / C ++). Wir können jedoch das Beste bekommen   Vorteile von zumindest einigen der C   Präprozessor-Funktionen in Java:   Konstanten und bedingte Kompilierung.

    
Andrew Hare 21.12.2009 22:23
quelle
1

Ich glaube nicht, dass es wirklich so etwas gibt. Die meisten echten Java-Benutzer werden Ihnen sagen, dass dies eine gute Sache ist, und dass eine bedingte Kompilierung zu fast allen Kosten vermieden werden sollte.

Ich bin nicht wirklich mit ihnen einverstanden ...

Sie können Konstanten verwenden, die aus der Kompilierzeile definiert werden können, und das wird einige Auswirkungen haben, aber nicht wirklich alle. (Zum Beispiel können Sie keine Dinge haben, die nicht kompilieren, aber Sie wollen immer noch, in #if 0 ... (und nein, Kommentare lösen nicht immer dieses Problem, weil das Verschachteln von Kommentaren knifflig sein kann ...) )).

Ich denke, dass die meisten Leute Ihnen sagen werden, dass Sie eine Form der Vererbung verwenden sollen, aber das kann auch sehr hässlich sein, mit viel wiederholtem Code ...

Das heißt, Sie können immer nur Ihre IDE einrichten, um Ihr Java durch den Preprozessor zu werfen, bevor Sie es an javac senden ...

    
Brian Postow 21.12.2009 22:31
quelle
1

"um meine ausführbare Dateigröße zu minimieren"

Was meinst du mit "ausführbare Größe"?

Wenn Sie die Menge an Code meinen, die zur Laufzeit geladen wird, können Sie Klassen über den Klassenlader bedingt laden. Sie verteilen also Ihren alternativen Code, egal was passiert, aber er wird nur geladen, wenn die Bibliothek, für die er steht, fehlt. Sie können einen Adapter (oder einen ähnlichen Adapter) verwenden, um die API zu kapseln, um sicherzustellen, dass fast Ihr gesamter Code genau gleich ist, und je nach Fall eine von zwei Wrapper-Klassen geladen wird. Der Java-Sicherheits-SPI könnte Ihnen einige Ideen geben, wie dies strukturiert und implementiert werden kann.

Wenn Sie die Größe Ihrer .jar-Datei meinen, können Sie das oben beschriebene tun, aber Ihren Entwicklern sagen, wie Sie die unnötigen Klassen aus dem Jar entfernen, falls sie wissen, dass sie nicht benötigt werden .

    
Steve Jessop 21.12.2009 22:43
quelle
0

Verwenden Sie Eigenschaften, um so etwas zu tun.

Verwenden Sie Dinge wie Class.forName, um die Klasse zu identifizieren.

Verwenden Sie keine if-Anweisungen, wenn Sie eine Eigenschaft direkt in eine Klasse übersetzen können.

    
S.Lott 21.12.2009 22:25
quelle
0

Je nachdem, was Sie tun (nicht genug Informationen), können Sie etwas tun:

%Vor%

und stellen Sie dann eine Klasse zur Verfügung, um die Instanziierung zu abstrahieren:

%Vor%

Dann führe java mit -Dfoo.name = RealFoo | FakeFoo

aus

Ignoriert die Ausnahmebehandlung in der makeFoo-Methode und Sie können es auf andere Weise tun ... aber die Idee ist die gleiche.

Auf diese Weise kompilieren Sie beide Versionen der Foo-Unterklassen und lassen den Entwickler zur Laufzeit wählen, welche er verwenden möchte.

    
TofuBeer 22.12.2009 01:24
quelle
0

Ich sehe, dass Sie hier zwei sich gegenseitig ausschließende Probleme angeben (oder, wahrscheinlicher, Sie haben eine ausgewählt und ich verstehe nur nicht, welche Wahl Sie getroffen haben).

Sie müssen eine Auswahl treffen: Versenden Sie zwei Versionen Ihres Quellcodes (einen, wenn die Bibliothek existiert, und einen, wenn dies nicht der Fall ist), oder versenden Sie eine einzelne Version und erwarten, dass sie mit der Bibliothek zusammenarbeitet Die Bibliothek existiert.

Wenn Sie möchten, dass eine einzelne Version die Existenz der Bibliothek erkennt und diese, falls verfügbar, verwendet, dann MÜSSEN Sie den gesamten Code haben, um in Ihrem verteilten Code darauf zugreifen zu können - Sie können ihn nicht ausschneiden. Da Sie Ihr Problem mit der Verwendung von #define gleichsetzen, ging ich davon aus, dass dies nicht Ihr Ziel war - Sie möchten 2 Versionen versenden (Die einzige Möglichkeit, wie #define funktionieren kann)

Also, mit 2 Versionen können Sie ein libraryInterface definieren. Dies kann entweder ein Objekt sein, das Ihre Bibliothek umschließt und alle Aufrufe an die Bibliothek für Sie oder eine Schnittstelle weiterleitet - in jedem Fall MUSS dieses Objekt zur Kompilierzeit für beide Modi vorhanden sein.

%Vor%

Wenn Sie nun Ihre Bibliothek verwenden möchten (Fälle, in denen Sie eine #ifdef in C gehabt hätten), haben Sie Folgendes:

%Vor%

Die Bibliothek ist eine Schnittstelle, die in beiden Fällen existiert. Da es immer durch LIBRARY_EXISTS geschützt ist, wird es kompiliert (sollte niemals in Ihren Klassenlader geladen werden - aber das ist implementierungsabhängig).

Wenn es sich bei Ihrer Bibliothek um eine vordefinierte Bibliothek handelt, die von einem Drittanbieter bereitgestellt wird, müssen Sie möglicherweise Library eine Wrapperklasse bereitstellen, die ihre Aufrufe an Ihre Bibliothek weiterleitet. Da Ihr Bibliothekswrapper niemals instanziiert wird, wenn LIBRARY_EXISTS false ist, sollte er nicht einmal zur Laufzeit geladen werden (Heck, es sollte nicht einmal kompiliert werden, wenn die JVM schlau genug ist, da sie immer durch eine letzte Konstante geschützt ist) dass der Wrapper in beiden Fällen zur Kompilierzeit verfügbar sein muss.

    
Bill K 22.12.2009 18:32
quelle
0

Wenn es hilft, j2me polish oder Using Präprozessordirektiven im BlackBerry JDE-Plugin für Eclipse?

Dies ist für Handys app, aber das kann wiederverwendet werden nein?

    
RzR 20.01.2010 14:53
quelle
0

Ich habe noch einen besseren Weg zu sagen.

Was Sie brauchen, ist eine letzte Variable.

%Vor%

Dann innerhalb des Codes sagen als

%Vor%

Dies wird als #ifdef funktionieren. Jeder der Blöcke wird im ausführbaren Code vorhanden sein. Andere werden in der Kompilierzeit selbst gelöscht

    
Vivek MVK 23.01.2015 18:32
quelle