2009-03-04 4 views
67

Ich bin sicher, dass Sie alle das Verhalten wissen, ich meine - Code wie:Warum ruft eine statische Methode über eine Instanz keinen Fehler für den Java-Compiler auf?

Thread thread = new Thread(); 
int activeCount = thread.activeCount(); 

eine Warnung Compiler provoziert. Warum ist es kein Fehler?

EDIT:

Um klar zu sein: Frage hat nichts mit Themen zu tun. Ich erkenne, dass Thread-Beispiele oft gegeben werden, wenn man darüber spricht, weil sie das Potenzial haben, Dinge wirklich zu vermasseln. Aber wirklich das Problem ist, dass solche Verwendung ist immer Unsinn und Sie können nicht (kompetent) so einen Anruf schreiben und es meinen. Ein Beispiel für diese Art von Methodenaufruf wäre belly. Hier ist eine andere:

String hello = "hello"; 
String number123AsString = hello.valueOf(123); 

, die sie als aussehen lässt, wenn jeder String-Instanz kommt mit einem "String valueOf (int i)" -Methode.

+11

Um auf Ihren Punkt zu erweitern, kann die redundante Instanz sogar null sein: 'String hallo = null; hallo.valueOf (123); 'funktioniert! – Thilo

Antwort

71

Grundsätzlich glaube ich, dass die Java-Entwickler einen Fehler gemacht haben, als sie die Sprache entworfen haben, und es ist zu spät, um es aufgrund der Kompatibilitätsprobleme zu beheben. Ja, es 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, IMO, behandelt wird. Sollten Sie jemals eine Sprache entwickeln sich, tragen sie im Sinn, als ein Beispiel für die Art der Sache zu vermeiden :)

Nur um DJClayworth der Punkt zu antworten, hier ist was in C# erlaubt ist:

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 tun Ich denke, es ist irreführend? Denn wenn ich mir Code someVariable.SomeMethod() anschaue, erwarte ich, dass er den Wert someVariable verwendet. Wenn eine statische Methode ist, ist diese Erwartung ungültig. Der Code trickst mich. Wie kann das eine gute Sache sein?

Seltsamerweise wird Java nicht zulassen, dass Sie eine potenziell nicht initialisierte Variable verwenden, um eine statische Methode aufzurufen, obwohl die einzige Information, die sie verwenden wird, der deklarierte Typ der Variablen ist. Es ist ein widersprüchliches und wenig hilfreiches Durcheinander. Warum erlauben Sie es?

EDIT: Diese Änderung ist eine Antwort auf Clayton's Antwort, die behauptet, dass Vererbung für statische Methoden ermöglicht. Es tut es nicht. Statische Methoden sind nicht polymorph. Hier ist ein kurzes, aber vollständiges Programm zu demonstrieren, dass:

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, der Ausführungszeitwert von b wird völlig ignoriert.

+3

nur wenn ich Java entworfen hätte! lol! –

+0

Ich denke, Sie könnten etwas verpassen. Siehe meinen Beitrag. – DJClayworth

+1

@DJClayworth: Nein. Das ist in C# erlaubt, aber someInstanceOfMyClass.doComplexCalculation() wäre nicht. –

13

Weil die Sprachspezifikation es erlaubt :) Genau wie VB tut und C# nicht.

+1

Die Spezifikation erlaubt es - aber gibt es irgendeinen Fall, wo es sinnvoll ist, es zu tun? – tmtest

+1

Es ist nur eine andere Art, es zu nennen. So können Sie auf Mitgliedsfelder mit oder ohne dieses Schlüsselwort zugreifen. Oder statische Methoden in derselben Klasse aufrufen, ohne einen Klassennamen anzugeben. Ich mag die C# -Wege viel besser und würde niemals einen solchen Code schreiben, aber die Sprachdesigner haben es so gestaltet. –

+0

@tmtest: foo() {bar (neue Basis()); } zoo() {bar (new Derived()); } bar (Basis b) {b.staticOverriddenInDerived(); } Sie möchten nicht, dass bar (Base) Base.staticOverriddenInDerived() aufruft; weil zur Laufzeit b ein Abgeleitetes mit einer überschriebenen statischen Methode sein könnte. Es ist eine mehrdeutige Notation, erlaubt aber Instanzmitglieder, auf die in einer Instanzmethode verwiesen wird, mit Namen, die nicht von denen unterschieden werden können, die nur für diese Methode deklariert sind (oder für einen Block darin, einschließlich einer verschachtelten Klasse). – Matthew

5

Kurze Antwort - die Sprache erlaubt es, also ist es kein Fehler.

8

Sie können es nicht mehr zu einem Fehler machen, wegen all des Codes, der bereits da draußen ist.

Ich bin bei dir, dass es ein Fehler sein sollte. Vielleicht sollte es eine Option/Profil für den Compiler geben, um einige Warnungen auf Fehler zu aktualisieren.

Update: Als sie das assert Stichwort in 1.4 eingeführt, die ähnlich mögliche Kompatibilitätsprobleme mit altem Code hat, sie machten es available only if you explicitly set the source mode to "1.4". 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 diese Schwierigkeiten entstehen würden. Wie andere betont haben, ist es nicht unbedingt notwendig, Sie daran zu hindern, verwirrenden Code zu schreiben. Und Sprachänderungen in Java sollten sich auf das unbedingt notwendige beschränken.

+0

Es gibt solche Option im Eclipse-Compiler (indirectStatic, http://help.eclipse.org/help33/index.jsp?topic=/org.eclipse.jdt.doc.isv/guide/jdt_api_compile.htm) –

+1

Aber sie haben dies, als sie das assert-Schlüsselwort in Java 1.4 einführten. Warum können sie es nicht wieder so machen? –

+0

Sie haben Recht. Für Assert müssen Sie den Compiler-Level speziell auf 1.4 setzen, ansonsten kompiliert er noch ohne Assert. Ich nehme an, 1,5 Anmerkungen und Generika funktionieren gleich. So könnte man eine Java 7 Kompilierstufe haben, die es zu einem Fehler machen würde. – Thilo

1

Wahrscheinlich können Sie es in Ihrem IDE ändern (in Eclipse Einstellungen -> Java -> Compiler -> Fehler/Warnungen)

0

Es gibt nicht Option für sie. In Java (wie viele andere Sprachen) können Sie über den Klassennamen oder das Instanzobjekt dieser Klasse auf alle statischen Member einer Klasse zugreifen. Das liegt an Ihnen und Ihrer Fall- und Softwarelösung, welche Sie verwenden sollten, die Ihnen mehr Lesbarkeit gibt.

1

wahrscheinlich für die gleichen logisch, dass diese nicht einen Fehler macht:

public class X 
{ 
    public static void foo() 
    { 
    } 

    public void bar() 
    { 
     foo(); // no need to do X.foo(); 
    } 
} 
2

Die wirklich wichtige Sache, aus der Perspektive des Compilers, ist, dass es in der Lage sein, Symbole zu lösen. Im Fall einer statischen Methode muss sie wissen, in welche Klasse sie hineinschauen soll - da sie keinem bestimmten Objekt zugeordnet ist. Die Entwickler von Java haben offensichtlich entschieden, dass sie die Klasse einer statischen Methode für dieses Objekt von jeder Instanz des Objekts auflösen können, da sie die Klasse eines Objekts bestimmen können. Sie entscheiden sich dafür, dies - vielleicht durch @ TofuBeers Beobachtung beeinflusst - dem Programmierer etwas Bequemlichkeit zu geben. Andere Sprachdesigner haben unterschiedliche Entscheidungen getroffen. Ich wäre wahrscheinlich in das letztere Lager gefallen, aber für mich ist das kein großer Deal.Ich würde wahrscheinlich die Verwendung erlauben, die @TofuBeer erwähnt, aber es erlaubt zu haben, dass meine Position, den Zugriff von einer Instanzvariablen nicht zuzulassen, weniger haltbar ist.

12

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 geben, ist sehr spezifisch Threads, nicht statische Methodenaufrufe. Es sieht so aus, als ob Sie die activeCount() für den Thread erhalten, auf den sich thread bezieht, aber Sie erhalten wirklich die Anzahl für den aufrufenden Thread. Dies ist ein logischer Fehler, den Sie als Programmierer machen. In diesem Fall muss der Compiler eine Warnung ausgeben. Es liegt an dir, die Warnung zu beachten und deinen Code zu reparieren.

EDIT: Ich weiß, dass die Syntax der Sprache ist, was erlaubt Sie irreführender Code schreiben, aber denken Sie daran, dass der Compiler und seine Warnungen Teil der Sprache auch sind. Die Sprache ermöglicht es Ihnen, etwas zu tun, was der Compiler als zweifelhaft betrachtet, aber Sie erhalten eine Warnung, um sicherzustellen, dass Sie wissen, dass dies zu Problemen führen kann.

+8

Es ist irrelevant für die * Instanz *. Das einzige ist der * deklarierte Typ * der Variablen. Es ist ein schreckliches, schreckliches Fehlverhalten und ein Fehler der Sprachdesigner IMO. –

+1

Ja, das ist für die Instanz irrelevant, aber ich denke nicht, dass das Aufrufen einer statischen Methode von einer Instanz ein vollwertiger Fehler sein sollte. Es ist einfach ein schlechter Stil. IMHO sollte eine Klasse entweder alle statischen Methoden sein, oder die statischen Methoden sollten privat sein, aber ich möchte nicht, dass der Compiler dies erzwingt. –

+1

@ Bill: Ich glaube nicht, dass es in den frühen Tagen sogar eine Warnung gab. Die Warnung wurde hinzugefügt, weil sie Leute irreführte.Was ist der * Vorteil * bei der Erlaubnis? –

3

Es ist kein Fehler, weil es Teil der Spezifikation ist, aber Sie fragen natürlich nach der Begründung, die wir alle erraten können.

Meine Vermutung ist, dass die Quelle von dies ist eigentlich eine Methode in einer Klasse zu ermöglichen, eine statische Methode in der gleichen Klasse ohne den Aufwand aufzurufen. Da das Aufrufen von x() legal ist (auch ohne den eigenen Klassennamen), sollte das Aufrufen von this.x() ebenso legal sein, und daher wurde das Aufrufen über irgendein Objekt ebenfalls legal gemacht.

Dies hilft Benutzern auch, private Funktionen in statische umzuwandeln, wenn sie den Status nicht ändern.

Außerdem versuchen Compiler im Allgemeinen, Fehler zu deklarieren, wenn dies nicht zu einem direkten Fehler führen kann. Da eine statische Methode den Status oder die Sorgfalt bezüglich des aufrufenden Objekts nicht ändert, führt dies nicht zu einem tatsächlichen Fehler (nur Verwirrung). Eine Warnung reicht aus.

+2

@Uri: Es ist einfach genug, es innerhalb der aktuellen Klasse verfügbar zu machen, ohne den Klassennamen anzugeben. C# hat es geschafft, und Java-Compiler schaffen es, den Unterschied zu erkennen (da Sie in diesem Fall keine Warnung erhalten). Warum sollte es so angegeben werden? –

2

Der Zweck der Instanzvariablenreferenz besteht nur darin, den Typ anzugeben, der die statische enthält. Wenn Sie sich den Byte-Code anschauen, der eine statische Verbindung über instance.staticMethod oder EnclosingClass aufruft, erzeugt staticMethod den gleichen Bytecode für die Methode der statischen Methode. Es wird kein Verweis auf die Instanz angezeigt.

Die Antwort als auch warum es da drin ist, nun, es ist einfach. Solange Sie die Klasse verwenden. und nicht über eine Instanz helfen Sie, Verwirrung in der Zukunft zu vermeiden.

-9

ich dies nur betrachten:

instanceVar.staticMethod(); 

für diese Kurzschrift sein:

instanceVar.getClass().staticMethod(); 

Wenn Sie immer das tun musste:

SomeClass.staticMethod(); 

dann würden Sie nicht sein in der Lage, die Vererbung für statische Methoden zu nutzen.

Das heißt, indem Sie die statische Methode über die Instanz aufrufen, müssen Sie nicht wissen, welche konkrete Klasse die Instanz zur Kompilierzeit ist, sondern nur, dass sie statisticMethod() irgendwo entlang der Vererbungskette implementiert.

EDIT: Diese Antwort ist falsch. Siehe Kommentare für Details.

+1

Gut, das ist ein weiterer Grund, warum es nicht erlaubt sein sollte - weil Sie auch in die Irre geführt werden. Es verhält sich nicht wie du es willst. Es wird * nur * den * deklarierten * Typ von "instanceVar" in Ihrem Beispiel verwenden. Verdammt, der Wert von instanceVar kann sogar null sein. –

+0

@Jon: Ich hatte den Eindruck, dass getClass() Ihnen die tatsächliche Klasse des Objekts, nicht den deklarierten Typ der Variablen geben würde. Javadoc ist zu diesem Thema unklar, also werde ich es testen. – Clayton

+1

@Clayton: Ja, aber getClass() wird nicht aufgerufen. Ich bearbeite meine Antwort mit einem Beispiel. –

Verwandte Themen