Как javac автоматически компилирует зависимости класса

Учитывая следующую структуру каталогов:

/top
   |--- wrk
          |--- pkg
                  |--- A.java
                  |--- B.java

Предположим, что два файла A.java и B.java содержат следующий код:

// Filename: A.java
package pkg;
class A { B b; }

// Filename: B.java
package pkg;
class B {...}

Предполагая, что текущий каталог /top/wrk

Почему команда javac -cp . pkg/A.java работает успешно, хотя мы еще не скомпилировали B.java?

Также, если текущий каталог /top/wrk/pkg, то работает команда javac A.java. Как так?

Ответ 1

Почему команда javac -cp. pkg/A.java успешно работает, хотя мы еще не скомпилировали B.java

Когда вы компилируете A.java, компилятор будет компилировать B.java, так как оба A.java и B.java находятся в одном пакете. Это будет работать даже если B.java находился в другом пакете от A.java (при условии, что B является общедоступным), если оба пакета присутствуют в каталоге wrk и вы скомпилируете A.java из каталога wrk.

Из документации Oracle для javac:

Если параметр -sourcepath не указан, путь к пользовательскому классу также выполняется для исходных файлов.

Из документа Oracle для CLASSPATH

Значение по умолчанию для пути к классу - "."

Если вы не установили CLASSPATH, по умолчанию будет .. Впоследствии sourcepath также будет ., так как по умолчанию sourcepath совпадает с CLASSPATH. Вы можете подтвердить, что исходный путь по умолчанию установлен на ., компилируя A.java с помощью javac -verbose -g pkg\A.java. Обратите внимание, что компилятор ищет в текущем каталоге файлы .java:

[parsing started pkg\A.java] [parsing completed 29ms] [search path for source files: [.]]

Чтобы подтвердить, что для параметра sourcepath установлено значение CLASSPATH, вы можете попробовать изменить CLASSPATH с помощью параметра -cp, компилируя A.java с помощью javac -cp C:\ -verbose -g pkg\A.java. A.java не будет компилироваться на этот раз, так как вы перезаписали CLASSPATH до C:\ и что то, что sourcepath будет по умолчанию. Это результат:

[parsing started pkg\A.java] [parsing completed 26ms] [search path for source files: [C:\]] pkg\A.java:3: cannot find symbol symbol : class B

Также, если текущий каталог -/top/wrk/pkg, тогда команда javac А. Ява работает. Как это так?

Это не будет работать независимо от того, присутствует ли B.class в pkg

Отказ от ответственности: Я могу только подтвердить это поведение в Windows, но я очень сомневаюсь, что он должен быть другим в других операционных системах.

Ответ 2

Компилятор должен либо найти, либо успешно скомпилировать источник для B, либо найти .class для B, даже если это просто импорт. В отличие от загрузки, которая выполняется динамически.

Посмотрите в свой выходной каталог, и вы увидите, что B тоже скомпилирован. Он будет скомпилирован, даже если в другом пакете, но вам придется сделать его общедоступным, чтобы ссылаться на него с помощью A.

Ответ 3

Из Oracle javac docs...

Если вы задаете параметр -sourcepath, то компилятор выполняет поиск указанный путь для исходных файлов. В противном случае компилятор выполняет поиск путь пользовательского класса для файлов классов и исходных файлов. В Windows параметр -sourcepath, по-видимому, установлен по умолчанию, и ваша команда работает.

На моем Mac, однако, он терпит неудачу и дает следующее сообщение...

A.java:5: error: cannot find symbol
    B b;
    ^
  symbol:   class B
  location: class A
1 error

Чтобы получить возможность автоматического поиска и компиляции исходных файлов зависимостей, вам необходимо использовать параметр -sourcepath. например...

javac -sourcepath ./* A.java