Proszę wyjaśnić następujące kwestie związane z błędami "Cannot find symbol" oraz "Cannot resolve symbol":
To pytanie jest zaprojektowane, aby zasiać kompleksowe Q&A na temat tych powszechnych błędów kompilacji w Javie.
Nie do końca. "Cannot find symbol" i "Cannot resolve symbol" oznaczają to samo. Niektóre kompilatory Javy używają jednego wyrażenia, a niektóre drugiego.
Po pierwsze, jest to błąd kompilacji1. Oznacza to, że albo istnieje problem w twoim kodzie źródłowym Java, albo istnieje problem w sposobie jego kompilacji. Twój kod źródłowy Java składa się z następujących rzeczy:
true
, false
, class
, while
, i tak dalej.42
i 'X'
oraz "Cześć mamo!"
.+
, =
, {
, i tak dalej.Reader
, i
, toString
, processEquibalancedElephants
, i tak dalej.W pierwszej kolejności, istnieje tylko jedna przyczyna. Kompilator szukał we wszystkich miejscach, gdzie identyfikator powinien być zdefiniowany, i nie mógł znaleźć definicji. Może to być spowodowane wieloma rzeczami. Najczęstsze z nich są następujące:
Dla identyfikatorów w ogóle:
StringBiulder
zamiast StringBuilder
. Java nie może i nie będzie próbować kompensować złej pisowni lub błędów w pisowni.stringBuilder
zamiast StringBuilder
. We wszystkich identyfikatorach Javy rozróżniana jest wielkość liter.mystring
i my_string
są różne. (Jeśli będziesz trzymał się zasad stylu Javy, będziesz w dużej mierze chroniony przed tym błędem...)Dla identyfikatorów, które powinny odnosić się do zmiennych:
Dla identyfikatorów, które powinny być nazwami metod lub pól:
Być może próbujesz odwołać się do dziedziczonej metody lub pola, które nie zostało'zadeklarowane w klasach macierzystych / przodkach lub interfejsach.
Być może próbujesz odwołać się do metody lub pola, które nie istnieje (tj. nie zostało zadeklarowane) w typie, którego używasz; np. "someString".push()
2.
Być może próbujesz użyć metody jako pola, lub odwrotnie; np. "someString".length
lub someArray.length()
.
Być może błędnie operujesz na tablicy, a nie na elemencie tablicy; np.
String strings[] = ...
if (strings.charAt(3)) { ... }
// może to powinno być 'strings[0].charAt(3)'
Dla identyfikatorów, które powinny być nazwami klas:
new
jak w:
String s = String(); // powinno być 'new String()'Dla przypadków, w których typ lub instancja nie ma członka, którego oczekiwałeś, że będzie miał:
java.io.*
a następnie próbowałeś użyć klasy Files
... która jest w java.nio
a nie java.io
. A może chciałeś napisać File
... który jest klasą w java.io
.Oto przykład, jak nieprawidłowe określenie zakresu zmiennych może prowadzić do błędu "Cannot find symbol":
List<String> strings = ...
for (int i = 0; i < strings.size(); i++) {
if (strings.get(i).equalsIgnoreCase("fnord")) {
break;
}
}
if (i < strings.size()) {
...
}
i
w instrukcji if
. Chociaż wcześniej zadeklarowaliśmy i
, ta deklaracja jest tylko w zakresie dla instrukcji for
i jej ciała. Odniesienie do i
w instrukcji if
nie widzi tej deklaracji i
. Jest ona out of scope.
(Odpowiednią poprawką może być tutaj przeniesienie instrukcji if
do środka pętli, lub zadeklarowanie i
przed rozpoczęciem pętli).Oto przykład, który powoduje zdziwienie, gdzie literówka prowadzi do pozornie niewytłumaczalnego błędu "Cannot find symbol":
for (int i = 0; i < 100; i++); {
System.out.println("i is " + i);
}
To da ci błąd kompilacji w wywołaniu println
mówiący, że i
nie może zostać znaleziony. Ale (słyszę, jak mówisz) zadeklarowałem to!
Problemem jest podstępny średnik ( ;
) przed {
. Składnia języka Java definiuje średnik w tym kontekście jako puste wyrażenie. Pusta instrukcja staje się wtedy ciałem pętli for
. Więc ten kod w rzeczywistości oznacza to:
for (int i = 0; i < 100; i++);
// The previous and following are separate statements!!
{
System.out.println("i is " + i);
}
{ ... }' blok NIE jest ciałem pętli
for, a zatem poprzednia deklaracja
iw instrukcji
for` jest out-of scope w bloku.Oto inny przykład błędu "Cannot find symbol", który jest spowodowany przez literówkę.
int tmp = ...
int res = tmp(a + b);
Pomimo wcześniejszej deklaracji, tmp
w wyrażeniu tmp(...)
jest błędne. Kompilator będzie szukał metody o nazwie tmp
, ale jej nie znajdzie. Wcześniej zadeklarowane tmp
znajduje się w przestrzeni nazw dla zmiennych, a nie w przestrzeni nazw dla metod.
W przykładzie, na który się natknąłem, programista faktycznie pominął operator. To, co chciał napisać, wyglądało tak:
int res = tmp * (a + b);
Foo
i Bar
, gdzie Foo
używa Bar
. Jeśli nigdy nie kompilowałeś Bar
i uruchamiasz javac Foo.java
, jesteś w stanie stwierdzić, że kompilator nie może'znaleźć symbolu Bar
. Prostą odpowiedzią jest skompilowanie Foo
i Bar
razem; np. javac Foo.java Bar.java
lub javac *.java
. Albo jeszcze lepiej użyć narzędzia do kompilacji Java; np. Ant, Maven, Gradle i tak dalej.
Istnieje kilka innych bardziej niejasnych przyczyn ... z którymi będę miał do czynienia poniżej.
3. Jak mogę naprawić te błędy ?Ogólnie rzecz biorąc, zaczynasz od ustalenia, co powodowało błąd kompilacji.
for (int i = 1; i < 10; i++) {
for (j = 1; j < 10; j++) {
...
}
}
Załóżmy, że kompilator mówi "Cannot find symbol" dla j
. Jest wiele sposobów, na które mógłbym to "naprawić":
for
na for (int j = 1; j < 10; j++)
- prawdopodobnie poprawne.j
przed wewnętrzną pętlą for
, lub zewnętrzną pętlą for
- prawdopodobnie poprawne.j
na i
w wewnętrznej pętli for
- prawdopodobnie błędnie!Oto kilka przypadków, w których "Cannot find symbol" jest pozornie niewytłumaczalny ... dopóki nie przyjrzysz się bliżej.
tar -tvf
aby wyświetlić zawartość podejrzanego pliku JAR.R
, bądź świadomy, że symbole R
są zdefiniowane przez plik context.xml
. Sprawdź czy twój plik context.xml
jest poprawny i we właściwym miejscu, oraz czy odpowiedni plik klasy R
został wygenerowany / skompilowany. Zauważ, że symbole Java rozróżniają wielkość liter, więc odpowiadające im identyfikatory XML również rozróżniają wielkość liter.
Inne błędy symboli na Androida mogą wynikać z wcześniej wymienionych powodów; np. brakujące lub nieprawidłowe zależności, nieprawidłowe nazwy pakietów, metody lub pola, które nie istnieją w danej wersji API, błędy pisowni i tak dalej.substring
jest nieznanym symbolem w czymś takim jak np.
String s = ...
String s1 = s.substring(1);
Okazało się, że programista stworzył własną wersję String
i jego wersja klasy nie definiowała metody substring
.
Lekcja: Nie definiuj własnych klas o takich samych nazwach jak wspólne klasy biblioteczne!uxxxx
dla innych znaków.1 - Jeśli przypadkiem widzisz to w wyjątkach lub komunikatach o błędach, to albo masz skonfigurowane IDE do uruchamiania kodu z błędami kompilacji, albo twoja aplikacja generuje i kompiluje kod ... w czasie runtime. 2 - Trzy podstawowe zasady Inżynierii Lądowej: woda nie płynie pod górę, deska jest mocniejsza na boku i nie można'pchać się na sznurek.
Ten błąd otrzymasz również, jeśli zapomnisz o new
:
String s = String();
versus
String s = new String();
ponieważ wywołanie bez słowa kluczowego new
będzie próbowało i szukało (lokalnej) metody o nazwie String
bez argumentów - a ta sygnatura metody prawdopodobnie nie jest zdefiniowana.
Jeszcze jeden przykład 'Zmienna jest poza zakresem' .
Ponieważ widziałem tego typu pytania już kilka razy, może jeszcze jeden przykład na to, co jest nielegalne, nawet jeśli może wydawać się w porządku.
Rozważmy ten kod:
if(somethingIsTrue()) {
String message = "Everything is fine";
} else {
String message = "We have an error";
}
System.out.println(message);
To jest niepoprawny kod. Ponieważ żadna ze zmiennych o nazwie message
nie jest widoczna poza swoim zakresem, którym w tym przypadku byłyby nawiasy {}
.
Można by powiedzieć: "Ale zmienna o nazwie wiadomość jest zdefiniowana tak czy inaczej - więc wiadomość jest zdefiniowana po if
".
Ale byłbyś w błędzie.
Java nie ma operatorów free()
lub delete
, więc musi polegać na śledzeniu zakresu zmiennych, aby dowiedzieć się, kiedy zmienne nie są już używane (wraz z odniesieniami do tych zmiennych z powodu).
Jest to szczególnie złe, jeśli myślałeś, że zrobiłeś coś dobrego. Widziałem ten rodzaj błędu po "optymalizacji" kodu w ten sposób:
if(somethingIsTrue()) {
String message = "Everything is fine";
System.out.println(message);
} else {
String message = "We have an error";
System.out.println(message);
}
"O, tam'jest zduplikowany kod, wyciągnijmy tę wspólną linię" -> i już.
Najczęstszym sposobem radzenia sobie z tego rodzaju problemami z zakresem byłoby wstępne przypisanie else-values do nazw zmiennych w zewnętrznym zakresie, a następnie ponowne przypisanie w if:
String message = "We have an error";
if(somethingIsTrue()) {
message = "Everything is fine";
}
System.out.println(message);