Ich bin sicher, Sie alle kennen das Verhalten, das ich meine - Code wie:
Thread thread = new Thread();
int activeCount = thread.activeCount();
provoziert eine Compiler-Warnung. Warum ist es kein Fehler?
BEARBEITEN:
Um es klar zu sagen: Frage hat nichts mit Threads zu tun. Mir ist klar, dass Thread-Beispiele oft gegeben werden, wenn dies diskutiert wird, weil das Potenzial besteht, die Dinge wirklich durcheinander zu bringen. Aber das Problem ist wirklich, dass eine solche Verwendung immer Unsinn ist und man einen solchen Anruf nicht (kompetent) schreiben und meinen kann. Jedes Beispiel für diese Art von Methodenaufruf wäre barmy. Hier ist ein anderes:
String hello = "hello";
String number123AsString = hello.valueOf(123);
Dadurch sieht es so aus, als ob jede String-Instanz mit einer "String valueOf (int i)" - Methode geliefert wird.
String hello = null; hello.valueOf(123);
funktioniert!Antworten:
Grundsätzlich glaube ich, dass die Java-Designer beim Entwerfen der Sprache einen Fehler gemacht haben, und es ist aufgrund der damit verbundenen Kompatibilitätsprobleme zu spät, ihn zu beheben. Ja, dies kann zu sehr irreführendem Code führen. Ja, du solltest es vermeiden. Ja, Sie sollten sicherstellen, dass Ihre IDE so konfiguriert ist, dass sie als Fehler behandelt wird, IMO. Sollten Sie jemals selbst eine Sprache entwerfen, denken Sie daran, wie Sie vermeiden sollten :)
Um auf den Punkt von DJClayworth zu antworten, ist in C # Folgendes erlaubt:
public class Foo { public static void Bar() { } } public class Abc { public void Test() { // Static methods in the same class and base classes // (and outer classes) are available, with no // qualification Def(); // Static methods in other classes are available via // the class name Foo.Bar(); Abc abc = new Abc(); // This would *not* be legal. It being legal has no benefit, // and just allows misleading code // abc.Def(); } public static void Def() { } }
Warum halte ich es für irreführend? Denn wenn ich mir Code anschaue,
someVariable.SomeMethod()
erwarte ich, dass er den Wert von verwendetsomeVariable
. WennSomeMethod()
es sich um eine statische Methode handelt, ist diese Erwartung ungültig. Der Code betrügt mich. Wie kann das möglicherweise eine gute Sache sein?Seltsamerweise lässt Java Sie keine potenziell nicht initialisierte Variable verwenden, um eine statische Methode aufzurufen, obwohl die einzige Information, die verwendet wird, der deklarierte Typ der Variablen ist. Es ist ein inkonsistentes und nicht hilfreiches Durcheinander. Warum es zulassen?
BEARBEITEN: Diese Bearbeitung ist eine Antwort auf Claytons Antwort, die behauptet, sie erlaube die Vererbung für statische Methoden. Das tut es nicht. Statische Methoden sind einfach nicht polymorph. Hier ist ein kurzes, aber vollständiges Programm, um dies zu demonstrieren:
class Base { static void foo() { System.out.println("Base.foo()"); } } class Derived extends Base { static void foo() { System.out.println("Derived.foo()"); } } public class Test { public static void main(String[] args) { Base b = new Derived(); b.foo(); // Prints "Base.foo()" b = null; b.foo(); // Still prints "Base.foo()" } }
Wie Sie sehen können, wird der Ausführungszeitwert von
b
vollständig ignoriert.quelle
Warum sollte es ein Fehler sein? Die Instanz hat Zugriff auf alle statischen Methoden. Die statischen Methoden können den Status der Instanz nicht ändern (der Versuch ist ein Kompilierungsfehler).
Das Problem mit dem bekannten Beispiel, das Sie angeben, ist sehr spezifisch für Threads und nicht für statische Methodenaufrufe. Es sieht so aus, als würden Sie die
activeCount()
für den Thread erhalten, auf den verwiesen wirdthread
, aber Sie erhalten wirklich die Anzahl für den aufrufenden Thread. Dies ist ein logischer Fehler, den Sie als Programmierer machen. In diesem Fall ist es für den Compiler angemessen, eine Warnung auszugeben. Es liegt an Ihnen, die Warnung zu beachten und Ihren Code zu korrigieren.EDIT: Mir ist klar, dass die Syntax der Sprache es Ihnen ermöglicht, irreführenden Code zu schreiben, aber denken Sie daran, dass der Compiler und seine Warnungen auch Teil der Sprache sind. Die Sprache ermöglicht es Ihnen, etwas zu tun, das der Compiler für zweifelhaft hält, aber es gibt Ihnen die Warnung, um sicherzustellen, dass Sie sich bewusst sind, dass es Probleme verursachen könnte.
quelle
Sie können es nicht mehr zu einem Fehler machen, da der gesamte Code bereits vorhanden ist.
Ich bin bei Ihnen, dass es ein Fehler sein sollte. Möglicherweise sollte es eine Option / ein Profil für den Compiler geben, um einige Warnungen auf Fehler zu aktualisieren.
Update: Als sie das Schlüsselwort assert in 1.4 einführten , das ähnliche potenzielle Kompatibilitätsprobleme mit altem Code aufweist, haben sie es nur verfügbar gemacht, wenn Sie den Quellmodus explizit auf "1.4" gesetzt haben . Ich nehme an, man könnte es in einem neuen Quellmodus "Java 7" zu einem Fehler machen. Aber ich bezweifle, dass sie es tun würden, wenn man bedenkt, dass all der Ärger, den es verursachen würde. Wie andere bereits betont haben, ist es nicht unbedingt erforderlich, Sie daran zu hindern, verwirrenden Code zu schreiben. Und Sprachänderungen an Java sollten an dieser Stelle auf das unbedingt Notwendige beschränkt sein.
quelle
Kurze Antwort - die Sprache erlaubt es, es ist also kein Fehler.
quelle
Wahrscheinlich für dieselbe Logik, die dies nicht zu einem Fehler macht:
public class X { public static void foo() { } public void bar() { foo(); // no need to do X.foo(); } }
quelle
Aus Sicht des Compilers ist es wirklich wichtig, dass Symbole aufgelöst werden können. Bei einer statischen Methode muss sie wissen, in welcher Klasse sie suchen soll, da sie keinem bestimmten Objekt zugeordnet ist. Die Designer von Java haben offensichtlich entschieden, dass sie, da sie die Klasse eines Objekts bestimmen können, auch die Klasse einer beliebigen statischen Methode für dieses Objekt aus einer beliebigen Instanz des Objekts auflösen können. Sie beschließen, dies zuzulassen - möglicherweise beeinflusst durch die Beobachtung von @ TofuBeer -, um dem Programmierer etwas Bequemlichkeit zu geben. Andere Sprachdesigner haben andere Entscheidungen getroffen. Ich wäre wahrscheinlich in das letztere Lager gefallen, aber für mich ist das keine große Sache. Ich würde wahrscheinlich die Verwendung erlauben, die @TofuBeer erwähnt,
quelle
Es ist kein Fehler, weil es Teil der Spezifikation ist, aber Sie fragen offensichtlich nach der Begründung, die wir alle erraten können.
Ich vermute, dass die Quelle dafür tatsächlich darin besteht, einer Methode in einer Klasse zu erlauben, eine statische Methode in derselben Klasse ohne Probleme aufzurufen. Da das Aufrufen von x () legal ist (auch ohne den Namen der Selbstklasse), sollte das Aufrufen von this.x () ebenfalls legal sein, und daher wurde das Aufrufen über ein beliebiges Objekt ebenfalls legalisiert.
Dies hilft auch Benutzern, private Funktionen in statische zu verwandeln, wenn sie den Status nicht ändern.
Außerdem versuchen Compiler im Allgemeinen, das Deklarieren von Fehlern zu vermeiden, wenn dies nicht zu einem direkten Fehler führen kann. Da eine statische Methode den Status oder die Pflege des aufrufenden Objekts nicht ändert, verursacht sie keinen tatsächlichen Fehler (nur Verwirrung), um dies zuzulassen. Eine Warnung reicht aus.
quelle
Der Zweck der Instanzvariablenreferenz besteht nur darin, den Typ anzugeben, der die Statik einschließt. Wenn Sie sich den Bytecode ansehen, der eine statische Methode über instance.staticMethod oder EnclosingClass.staticMethod aufruft, wird der gleiche aufgerufene statische Methodenbytecode erzeugt. Es wird kein Verweis auf die Instanz angezeigt.
Die Antwort auch, warum es dort drin ist, nun, es ist einfach so. Solange Sie die Klasse verwenden. und nicht über eine Instanz helfen Sie, Verwirrung in Zukunft zu vermeiden.
quelle
Wahrscheinlich können Sie es in Ihrer IDE ändern (in Eclipse-Einstellungen -> Java -> Compiler -> Fehler / Warnungen)
quelle
Es gibt keine Option dafür. In Java (wie in vielen anderen Sprachen) können Sie über den Klassennamen oder das Instanzobjekt dieser Klasse auf alle statischen Mitglieder einer Klasse zugreifen. Es liegt an Ihnen und Ihrer Fall- und Softwarelösung, welche Sie verwenden sollten, um die Lesbarkeit zu verbessern.
quelle
Es ist ein ziemlich altes Thema, aber immer noch auf dem neuesten Stand und bringt heutzutage überraschenderweise eine größere Wirkung. Wie Jon erwähnte, könnte es nur ein Fehler sein, den Javas Designer am Anfang gemacht haben. Aber ich würde es mir nicht vorstellen, bevor es Auswirkungen auf die Sicherheit haben kann.
Viele Programmierer kennen Apache Velocity, eine flexible und leistungsstarke Template-Engine. Es ist so leistungsfähig, dass es die Vorlage von Vorlagen mit einer Reihe benannter Objekte ermöglicht - streng genommen als Objekte aus der Programmiersprache (ursprünglich Java). Auf diese Objekte kann wie in der Programmiersprache innerhalb einer Vorlage zugegriffen werden, sodass beispielsweise die String-Instanz von Java mit all ihren öffentlichen Feldern, Eigenschaften und Methoden verwendet werden kann
Dabei ist die Eingabe ein String , läuft direkt über JVM und gibt true oder false an die Ausgabe des Velocity-Parsers zurück. So weit, ist es gut.
In Java erben jedoch alle Objekte von Object, sodass unsere Endbenutzer dies auch in die Vorlage einfügen können
um eine Instanz von String Class zu erhalten.
Und mit dieser Referenz können sie auch einen Aufruf statische Methode forName (String) auf diese
$input.getClass().forName("java.io.FileDescriptor")
Verwenden Sie einen beliebigen Klassennamen und verwenden Sie ihn für jedes Konto des Webservers (Verunstalten, DB-Inhalt stehlen, Konfigurationsdateien überprüfen, ...).
Dieser Exploit wird hier (in einem bestimmten Kontext) beschrieben: https://github.com/veracode-research/solr-injection#7-cve-2019-17558-rce-via-velocity-template-by-_s00py
Es wäre nicht möglich, wenn das Aufrufen statischer Methoden aus der Referenz auf die Instanz der Klasse verboten wäre.
Ich sage nicht, dass ein bestimmtes Programmierframework besser ist als das andere oder so, aber ich möchte nur einen Vergleich anstellen. Es gibt eine Portierung von Apache Velocity für .NET. In C # ist es nicht möglich, statische Methoden nur aus der Referenz der Instanz aufzurufen, was einen solchen Exploit nutzlos macht:
$input.GetType().GetType("System.IO.FileStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
quelle
Ich denke nur an Folgendes:
dafür eine Abkürzung sein:
Wenn Sie dies immer tun müssten:
Dann könnten Sie die Vererbung nicht für statische Methoden nutzen.
Das heißt, wenn Sie die statische Methode über die Instanz aufrufen, müssen Sie nicht wissen, welche konkrete Klasse die Instanz zur Kompilierungszeit ist, sondern nur, dass sie staticMethod () irgendwo entlang der Vererbungskette implementiert.
EDIT: Diese Antwort ist falsch. Siehe Kommentare für Details.
quelle