Woher wissen Compiler über andere Klassen und ihre Eigenschaften Bescheid?


14

Ich schreibe meine erste Programmiersprache, die objektorientiert ist und bisher so gut darin ist, eine einzelne "Klasse" zu erstellen. Aber nehmen wir an, ich möchte Unterricht haben, sagen wir ClassAund ClassB. Vorausgesetzt, diese beiden haben nichts miteinander zu tun, ist alles in Ordnung. Angenommen , ClassAerstellt ein - ClassBdies wirft jedoch zwei verwandte Fragen auf:

-Wie würde der Compiler beim Kompilieren wissen, ClassAdass es ClassBüberhaupt existiert, und woher weiß er, ob es Eigenschaften gibt?

Meine bisherigen Gedanken waren: Anstatt jede Klasse zu einem Zeitpunkt zu kompilieren (dh zu scannen, zu analysieren und Code zu generieren), muss ich jede "Datei" (nicht wirklich Datei an sich, sondern eine "Klasse") zuerst scannen + analysieren , dann Code für alle generieren?

Antworten:


13

Verschiedene Sprachen (und damit Compiler) gehen unterschiedlich damit um.

In der C-Familie haben die verschiedenen Module eine entsprechende Header-Datei, die beim Erstellen des Objekts verwendet wird. Die Header-Dateien enthalten Informationen zur Größe des Objekts und zu den Funktionen oder Methoden, die möglicherweise aufgerufen werden. Dies ermöglicht die notwendigen Informationen für die Speicherzuweisung und "Existiert diese Methode / Funktion / Prozedur?" Dies wird verwendet, wenn eine einzelne Unit kompiliert wird, die keinen Zugriff auf die Quelle selbst benötigt.

In Java sind dem Compiler die Elemente in seinem Klassenpfad bekannt und er überprüft diese Objekte, um sie mit ihnen zu verknüpfen (er überprüft, ob Methoden vorhanden sind, über die richtige Anzahl von Argumenten verfügt usw.). Java kann auch zur Laufzeit in anderen Klassen, von denen es nichts weiß, wann es kompiliert wurde, eine dynamische Verknüpfung herstellen. In Class.forName finden Sie ein Beispiel für das dynamische Laden.

Beide Optionen sind durchaus gültig und haben ihre eigenen Vor- und Nachteile. Das Bereitstellen von Header-Dateien wird von manchen als umständlich empfunden und verletzt DRY . Wenn Sie keine Header-Dateien haben, müssen Bibliotheken vom Compiler und Linker inspiziert werden können - eine .so oder .dll-Datei enthält wahrscheinlich nicht genügend Informationen, um die Objekte ordnungsgemäß zu instanziieren oder die Methodenaufrufe zu validieren ( und wäre maschinenabhängig).


0

Praktisch betrachtet die IDE mit Java ein ganzes Programm auf einmal. Wenn ClassB referenziert wird, wird es vom IDE-Compiler überprüft. Alles, einschließlich der Bibliotheken, ist ein vollständiges Ganzes. Sobald das Programm fertig ist, können Sie Klassenpfade ändern, einzelne .class-Dateien ein- und auslagern und die Bibliotheksversion wechseln. Sie können auch einzelne .java-Dateien kompilieren, ohne die IDE zu verwenden (oder die Prüfungen zu umgehen). Das Ergebnis muss überhaupt nicht konsistent sein. Wenn dies nicht der Fall ist, erhalten Sie Laufzeitausnahmen. (Eine der vielen Aufgaben einer IDE besteht darin, Laufzeitfehler in Fehler zur Kompilierungszeit bzw. Bearbeitungszeit umzuwandeln.)

C # ist ungefähr gleich, und ich denke nicht, dass C und C ++ wirklich so verschieden sind, da das, was die Java- und C # -IDEs tun, nur C / C ++ - Header für Sie hinter den Kulissen erstellt.


Java-Compiler kompilieren bei Bedarf auch abhängige Inhalte. Sie erhalten nur Laufzeitausnahmen von dieser Art von Dingen, wenn Sie sich wirklich sehr bemüht haben, Dinge zu erzwingen (z. B. eine API zu ändern und neu zu kompilieren, nachdem Sie die Konsumenten dieser API kompiliert haben).
Donal Fellows

@DonalFellows: Bei meinen Problemen fehlten Bibliotheken ("Aber ich habe diese auf allen meinen Computern installiert!") Und laufende Programme wurden neu kompiliert (unbeabsichtigt .class-Dateien im laufenden Betrieb ausgetauscht). Ich kann das Aktualisieren von Paketen vorhersehen, die nicht mehr mit dem Hauptprogramm übereinstimmen, obwohl ich es noch nicht getan habe. Ich habe vor Jahren eine Menge davon mit C und .dll gemacht (und hatte es mir angetan). Ich glaube und hoffe, dass es jetzt viele Schutzmaßnahmen gibt, die es damals noch nicht gab.
RalphChapin

Ich verstehe wirklich nicht, was eine IDE damit zu tun hat, wie ein Compiler / Linker Compile / Linker-Probleme lösen kann. Korrigieren Sie mich, wenn ich falsch liege, aber eine IDE ist völlig orthogonal zum gesamten Problem (mit der Ausnahme, dass dies dem Programmierer die Arbeit erleichtert). Warum? Denn theoretisch verwendet die IDE den Compiler im Hintergrund.
Thomas Eding

@ThomasEding: Du hast ganz recht. Als ich diese Frage beantwortete, schien es Probleme zu geben, die IDE vom Compiler / Linker zu trennen. Meine Entschuldigung: Java "verknüpft" nicht, bis es ausgeführt wird, und kann daher nicht wissen, dass ein Klassenreferenz- oder Methodenaufruf bis dahin falsch ist. Die IDE "erleichtert" meine Aufgabe nicht wirklich, sie macht es möglich. Ich habe (in FORTRAN und C vor langer Zeit) verwendet, um Fehler beim Kompilieren, beim Verknüpfen und beim Ausführen zu erhalten. Jetzt bekomme ich fast alle diese Fehler richtig, wenn ich sie eintippe, was die IDE in gewissem Sinne zu einem Compiler, Linker und Executer macht . Heutzutage stammen alle Nicht-Laufzeitfehler aus der IDE.
RalphChapin

0

Ältere Sprachen sind manchmal strenger; Überlegen Sie, was in Java möglich ist:

public interface Ifc {
    public static final Ifc MY_CONSTANT = new Implem();
}

public class Implem implements Ifc {
}

Ich habe das Anti-Pattern oben gesehen und es ist wirklich hässlich (ich hätte es verboten). Beide Zusammenstellungseinheiten verwenden sich gegenseitig. Ifc kann jedoch zu Code kompiliert werden, ohne dass ein Implem kompiliert wurde. Der kompilierte Code .class, der mit einem C .obj vergleichbar ist, enthält "Verknüpfungsinformationen": einen Implem-Import, der einen parameterlosen Konstruktor aufruft Implem(). Die Implem-Klasse kann dann problemlos kompiliert werden. Teilweise dient der ClassLoader, der die Initialisierung / Erstellung von JVM-Klassendaten durchführt, und teilweise die Java Virtual Machine selbst als Linker , der alle integriert.

Beispielsweise erkennt das Kompilieren mit einer Version einer bestimmten Bibliothek und das Ausführen mit einer anderen Version dieser Bibliothek Laufzeitfehler.

Die Antwort lautet also: Compilation liefert Einheiten kompilierten Objektcodes, die man als Code + Daten + API zum Verknüpfen ansehen muss.

Der Compiler sollte anschließend auch ein Packen durchführen und die Verknüpfungs-API überprüfen . eine zweite Phase.

Dies mag irritieren und unelegant aussehen, aber mathematische Beweise können auf die gleiche Weise funktionieren: Wenn man die gesamte Richtigkeit beweist, kann man bereits einen Teil als wahr betrachten, der bis zur Verifikation gültig ist.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.