Вопрос-ответ

What does a "Cannot find symbol" or "Cannot resolve symbol" error mean?

Что означает ошибка "Не удается найти символ" или "Не удается разрешить символ"?

Пожалуйста, объясните следующее об ошибках "Не удается найти символ", "Не удается разрешить символ" или "Символ не найден" (в Java):


  • Что они означают?

  • Что может их вызвать?

  • Как программист собирается их исправлять?

Этот вопрос предназначен для заполнения исчерпывающих вопросов и ответов об этих распространенных ошибках компиляции в Java.

Переведено автоматически
Ответ 1

0. Есть ли какая-либо разница между этими ошибками?

Не совсем. "Не удается найти символ", "Не удается разрешить символ" и "Символ не найден" означают одно и то же. (Разные компиляторы Java написаны разными людьми, и разные люди используют разные фразеологии, чтобы сказать одно и то же.)

1. Что означает ошибка "Не удается найти символ"?

Во-первых, это ошибка компиляции1. Это означает, что либо проблема в вашем исходном коде Java, либо проблема в способе его компиляции.

Ваш исходный код Java состоит из следующих элементов:


  • Ключевые слова: like class, while и так далее.

  • Литералы: такие, как true, false, 42, 'X' и "Hi mum!".

  • Операторы и другие не буквенно-цифровые маркеры: like +, =, { и так далее.

  • Идентификаторы: like Reader, i, toString, processEquibalancedElephants и так далее.

  • Комментарии и пробелы.

Ошибка "Не удается найти символ" связана с идентификаторами. Когда ваш код скомпилирован, компилятору необходимо определить, что означает каждый идентификатор в вашем коде.

Ошибка "Не удается найти символ" означает, что компилятор не может этого сделать. Похоже, что ваш код ссылается на что-то, чего компилятор не понимает.

2. Что может вызвать ошибку "Не удается найти символ"?

В первую очередь, причина только одна. Компилятор просмотрел все места, где должен быть определен идентификатор, и не смог найти определение. Это может быть вызвано рядом причин. Наиболее распространенными из них являются следующие:


  • Для идентификаторов в целом:



    • Возможно, вы написали имя неправильно; т.е. StringBiulder Вместо StringBuilder. Java не может и не будет пытаться компенсировать неправильные орфографические или опечатки.

    • Возможно, вы перепутали регистр; т.е. stringBuilder Вместо StringBuilder. Все идентификаторы Java чувствительны к регистру.

    • Возможно, вы неправильно использовали символы подчеркивания; т.е. mystring и my_string отличаются друг от друга. (Если вы будете придерживаться правил стиля Java, вы будете в значительной степени защищены от этой ошибки ...)

    • Возможно, вы пытаетесь использовать что-то, что было объявлено "где-то еще"; т. Е. В другом контексте, отличном от того, в котором вы неявно указали компилятору искать. (Другой класс? Другая область видимости? Другой пакет? Другая кодовая база?)



  • Для идентификаторов, которые должны ссылаться на переменные:



    • Возможно, вы забыли объявить переменную.

    • Возможно, объявление переменной находится вне области видимости в тот момент, когда вы пытались ее использовать. (Смотрите Пример ниже)



  • Для идентификаторов, которые должны быть именами методов или полей:



    • Возможно, вы пытаетесь сослаться на унаследованный метод или поле, которые не были объявлены в родительских / предковых классах или интерфейсах.



    • Возможно, вы пытаетесь сослаться на метод или поле, которые не существуют (т. Е. Не были Объявлены) в используемом вами типе; например, "rope".push()2.



    • Возможно, вы пытаетесь использовать метод в качестве поля или наоборот; например, "rope".length или someArray.length().



    • Возможно, вы по ошибке работаете с массивом, а не с элементом массива; например,


          String strings[] = ...
      if (strings.charAt(3)) { ... }
      // Maybe that should be 'strings[0].charAt(3)'




  • Для идентификаторов, которые должны быть именами классов:



    • Возможно, вы забыли импортировать класс.



    • Возможно, вы использовали импорт "star", но класс не определен ни в одном из импортированных вами пакетов.



    • Возможно, вы забыли new как в:


          String s = String();  // Should be 'new String()'


    • Возможно, вы пытаетесь импортировать или иным образом использовать класс, который был объявлен в пакете по умолчанию; т. Е. Тот, куда отправляются классы без package инструкций.


      Подсказка: узнайте о пакетах. Вы должны использовать пакет по умолчанию только для простых приложений, состоящих из одного класса ... или, в крайнем случае, один исходный файл Java.





  • Для случаев, когда тип или экземпляр, по-видимому, не имеют элемента (например, метода или поля), который вы ожидали от него иметь:



    • Возможно, вы объявили вложенный класс или универсальный параметр, который затеняет тип, который вы намеревались использовать.

    • Возможно, вы затеняете статическую переменную или переменный экземпляра.

    • Возможно, вы импортировали неправильный тип; например, из-за завершения разработки IDE или автокоррекции могло быть предложено java.awt.List, а не java.util.List.

    • Возможно, вы используете (компилируете) неправильную версию API.

    • Возможно, вы забыли привести свой объект к соответствующему подклассу.

    • Возможно, вы объявили тип переменной супертипом той, у которой есть элемент, который вы ищете.



Проблема часто заключается в сочетании вышеперечисленного. Например, возможно, вы импортировали "звездочку" java.io.*, а затем попытались использовать Files класс ... который находится в java.nio не java.io. Или, может быть, вы имели в виду написать File ... который является классом в java.io.


Вот пример того, как неправильное определение области видимости переменной может привести к ошибке "Не удается найти символ":

List<String> strings = ...

for (int i = 0; i < strings.size(); i++) {
if (strings.get(i).equalsIgnoreCase("fnord")) {
break;
}
}
if (i < strings.size()) {
...
}

Это выдаст ошибку "Не удается найти символ" для i в if инструкции. Хотя мы объявили ранее i, это объявление относится только к области видимости для for инструкции и ее тела. Ссылка на i в if инструкции не может видеть это объявление i. Это выходит за рамки.

(Подходящим исправлением здесь может быть перемещение if оператора внутри цикла или объявление i перед началом цикла.)


Вот пример, вызывающий недоумение, когда опечатка приводит к, казалось бы, необъяснимой ошибке "Не удается найти символ":

for (int i = 0; i < 100; i++); {
System.out.println("i is " + i);
}

Это выдаст вам ошибку компиляции в println вызове, сообщающую, что i не удается найти. Но (я слышу, как вы говорите) Я объявил это!

Проблема в скрытой точке с запятой ( ; ) перед {. Синтаксис языка Java определяет точку с запятой в этом контексте как пустой оператор. Затем пустой оператор становится телом for цикла. Итак, этот код на самом деле означает это:

for (int i = 0; i < 100; i++);

// The previous and following are separate statements!!

{
System.out.println("i is " + i);
}

{ ... } Блок НЕ является телом for цикла, и, следовательно, предыдущее объявление i в for инструкции находится вне области действия в блоке.


Вот еще один пример ошибки "Не удается найти символ", вызванной опечаткой.

int tmp = ...
int res = tmp(a + b);

Несмотря на предыдущее объявление, tmp в tmp(...) выражении ошибочно. Компилятор будет искать вызываемый метод tmp и не найдет его. Ранее объявленное tmp находится в пространстве имен для переменных, а не в пространстве имен для методов.

В примере, с которым я столкнулся, программист фактически пропустил оператор. Он хотел написать следующее:

int res = tmp * (a + b);

Есть еще одна причина, по которой компилятор может не найти символ, если вы выполняете компиляцию из командной строки. Возможно, вы просто забыли скомпилировать или перекомпилировать какой-то другой класс. Например, если у вас есть классы Foo и Bar где Foo используется Bar. Если вы никогда не компилировались Bar и вы запускаете javac Foo.java, вы можете обнаружить, что компилятор не может найти символ Bar. Простой ответ - скомпилировать Foo и Bar вместе; например, javac Foo.java Bar.java или javac *.java. Или еще лучше использовать инструмент сборки Java; например, Ant, Maven, Gradle и так далее.

Есть и другие, более неясные причины ... о которых я расскажу ниже.

3. Как мне исправить эти ошибки?

Вообще говоря, вы начинаете с выяснения того, что вызвало ошибку компиляции.


  • Посмотрите на строку в файле, указанную в сообщении об ошибке компиляции.

  • Определите, о каком символе идет речь в сообщении об ошибке.

  • Выясните, почему компилятор сообщает, что не может найти символ; смотрите выше!

Затем вы думаете о том, что должен говорить ваш код. Затем, наконец, вы решаете, какое исправление вам нужно внести в свой исходный код, чтобы делать то, что вы хотите.

Обратите внимание, что не каждое "исправление" является правильным. Учтите это:

for (int i = 1; i < 10; i++) {
for (j = 1; j < 10; j++) {
...
}
}

Предположим, что компилятор сообщает "Не удается найти символ" для j. Есть много способов, которыми я мог бы это "исправить":


  • Я мог бы изменить внутреннее значение for на for (int j = 1; j < 10; j++) - вероятно, правильно.

  • Я мог бы добавить объявление для j перед внутренним for циклом или внешним for циклом - возможно, правильно.

  • Я мог бы изменить j на i во внутреннем for цикле - вероятно, неправильно!

  • и так далее.

Дело в том, что вам нужно понимать, что пытается сделать ваш код, чтобы найти правильное исправление.

4. Неясные причины

Вот пара случаев, когда "Не удается найти символ" кажется необъяснимым ... пока вы не присмотритесь повнимательнее.


  1. Неправильные зависимости: Если вы используете IDE или инструмент сборки, который управляет путем сборки и зависимостями проекта, возможно, вы допустили ошибку с зависимостями; например, пропустили зависимость или выбрали неправильную версию. Если вы используете инструмент сборки (Ant, Maven, Gradle и т.д.), проверьте файл сборки проекта. Если вы используете IDE, проверьте конфигурацию пути сборки проекта.



  2. Не удается найти символ 'var': вероятно, вы пытаетесь скомпилировать исходный код, который использует вывод типа локальной переменной (т. Е. var Объявление) с более старым компилятором или более ранним --source уровнем. var Была введена в Java 10. Проверьте свою версию JDK и файлы сборки, а также (если это происходит в IDE) настройки IDE.



  3. Вы не компилируете / перекомпилируете: иногда случается, что начинающие Java-программисты не понимают, как работает цепочка инструментов Java, или не реализовали повторяющийся "процесс сборки"; например, используя IDE, Ant, Maven, Gradle и так далее. В такой ситуации программист может в конечном итоге гоняться за своим хвостом в поисках иллюзорной ошибки, которая на самом деле вызвана неправильной перекомпиляцией кода и тому подобное.


    Другой пример этого - когда вы используете (Java 9+) java SomeClass.java для компиляции и запуска класса. Если класс зависит от другого класса, который вы не компилировали (или перекомпилировали), вы можете получить ошибки "Не удается разрешить символ", относящиеся ко 2-му классу. Другие исходные файлы не компилируются автоматически. Новый режим "компиляции и запуска" команды java не предназначен для запуска программ с несколькими файлами исходного кода.



  4. Проблема с более ранней сборкой: Возможно, что более ранняя сборка завершилась ошибкой, в результате чего был получен файл JAR с отсутствующими классами. Такой сбой обычно можно было бы заметить, если бы вы использовали инструмент сборки. Однако, если вы получаете файлы JAR от кого-то другого, вы зависите от них правильной сборки и замечаете ошибки. Если вы подозреваете это, используйте tar -tvf, чтобы перечислить содержимое подозрительного файла JAR.



  5. Проблемы с IDE: пользователи сообщали о случаях, когда их IDE запутывается, и компилятор в IDE не может найти существующий класс ... или обратная ситуация.



    • Это может произойти, если IDE была настроена с неверной версией JDK.



    • Это может произойти, если кэши IDE не синхронизированы с файловой системой. Существуют специальные способы IDE исправить это.



    • Это может быть ошибка IDE. Например, @Joel Costigliola описал сценарий, в котором Eclipse неправильно обработал "тестовое" дерево Maven: смотрите этот ответ . (По-видимому, эта конкретная ошибка была исправлена давным-давно.)





  6. Проблемы с Android: Когда вы программируете для Android и у вас возникают ошибки "Не удается найти символ", связанные с R, имейте в виду, что R символы определяются context.xml файлом. Убедитесь, что ваш context.xml файл правильный и находится в нужном месте, и что соответствующий R файл класса был сгенерирован / скомпилирован. Обратите внимание, что символы Java чувствительны к регистру, поэтому соответствующие XML-идентификаторы также чувствительны к регистру.


    Другие ошибки с символами на Android, вероятно, вызваны ранее упомянутыми причинами; например, отсутствующие или неправильные зависимости, неправильные имена пакетов, метод или поля, которых нет в конкретной версии API, орфографические ошибки и так далее.



  7. Скрытие системных классов: я видел случаи, когда компилятор жаловался, что substring является неизвестным символом в чем-то вроде следующего


    String s = ...
    String s1 = s.substring(1);

    Оказалось, что программист создал свою собственную версию String и что его версия класса не определяла substring методы. Я видел, как люди делали это с System, Scanner и другими классами.


    Урок: не определяйте свои собственные классы с теми же именами, что и обычные библиотечные классы!


    Проблему также можно решить, используя полные имена. Например, в приведенном выше примере программист мог написать:


    java.lang.String s = ...
    java.lang.String s1 = s.substring(1);


  8. Гомоглифы: Если вы используете кодировку UTF-8 для своих исходных файлов, возможно, идентификаторы, которые выглядят одинаково, но на самом деле отличаются, потому что содержат гомоглифы. Смотрите Эту страницу для получения дополнительной информации.


    Вы можете избежать этого, ограничившись ASCII или Latin-1 в качестве кодировки исходного файла и используя Java \uxxxx экранирование для других символов.




1 - Если, возможно, вы действительно видите это в виде исключения во время выполнения или сообщения об ошибке, то либо вы настроили свою IDE для запуска кода с ошибками компиляции, либо ваше приложение генерирует и компилирует код .. во время выполнения.
2 - Три основных принципа гражданского строительства: вода не течет в гору, доска прочнее с одной стороны, и вы не можете надавить на веревку.

Ответ 2

Вы также получите эту ошибку, если забудете new:

String s = String();

против

String s = new String();

потому что вызов без new ключевого слова попытается найти (локальный) метод, вызываемый String без аргументов, и сигнатура этого метода, вероятно, не определена.

Ответ 3

РЕШЕНО

Использование IntelliJ

Выберите Build->Rebuild Project, чтобы решить проблему

Ответ 4

Еще один пример "Переменная находится вне области видимости"

Поскольку я уже несколько раз сталкивался с такого рода вопросами, возможно, еще один пример того, что является незаконным, даже если это может показаться нормальным.

Рассмотрим этот код:

if(somethingIsTrue()) {
String message = "Everything is fine";
} else {
String message = "We have an error";
}
System.out.println(message);

Это недопустимый код. Потому что ни одна из названных переменных message не видна за пределами их соответствующей области видимости, которая в данном случае была бы заключена в квадратные скобки {}.

Вы могли бы сказать: "Но переменная с именем message определяется в любом случае - поэтому message определяется после if".

Но вы были бы неправы.

В Java нет операторов free() or delete, поэтому ей приходится полагаться на отслеживание области видимости переменных, чтобы выяснить, когда переменные больше не используются (вместе со ссылками на эти переменные причины).

Это особенно плохо, если вы думали, что сделали что-то хорошее. Я видел такого рода ошибки после "оптимизации" кода, подобного этому:

if(somethingIsTrue()) {
String message = "Everything is fine";
System.out.println(message);
} else {
String message = "We have an error";
System.out.println(message);
}

"О, там дублированный код, давайте вытащим эту общую строку" -> и вот оно что.

Наиболее распространенным способом решения такого рода проблем с областью видимости было бы предварительно присвоить значения else именам переменных во внешней области видимости, а затем переназначить в if:

String message = "We have an error";
if(somethingIsTrue()) {
message = "Everything is fine";
}
System.out.println(message);
java