16

Given the following directory structure:

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

Assume that the two files A.java and B.java contain the following code, respectively:

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

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

Assuming that the current directory is /top/wrk

Why does the command javac -cp . pkg/A.java work successfully even though we have not yet compiled B.java?

Also if the current directory is /top/wrk/pkg then the command javac A.java works. How so?

Chetan Kinger
  • 15,069
  • 6
  • 45
  • 82
paidedly
  • 1,413
  • 1
  • 13
  • 22
  • The answer lies in http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javac.html#BHCCHDGH . No time to reproduce this here in form of an answer, for short, roughly: It also looks for source files in the classpath. – Marco13 May 29 '15 at 11:06
  • I'm unable to reproduce this. It fails to compile on my machine. Are you sure that B has never been compiled? – Phil Anderson May 29 '15 at 11:11
  • It's a Mac / Windows thing. See my edited and now (hopefully) definitive answer for more detail. – Phil Anderson May 29 '15 at 11:36

3 Answers3

10

Why does the command javac -cp . pkg/A.java work successfully even though we have not yet compiled B.java

When you compile A.java, the compiler will compile B.java as well since both A.java and B.java are in the same package. This will work even If B.java was in a different package from A.java (provided B is public) as long as both the packages are present in the wrk directory and you compile A.java from wrk directory.

From the Oracle documentation for javac :

If the -sourcepath option is not specified, the user class path is also searched for source files.

From the Oracle document for CLASSPATH

The default value of the class path is "."

If you haven't set a CLASSPATH, it will be defaulted to .. Subsequently, the sourcepath will also be . since the default sourcepath is the same as the CLASSPATH. You can confirm that the default sourcepath is set to . by compiling A.java using javac -verbose -g pkg\A.java. Notice that the compiler is looking in the current directory for .java files :

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

To confirm that the sourcepath is set to CLASSPATH, you can try changing the CLASSPATH using the -cp option by compiling A.java using javac -cp C:\ -verbose -g pkg\A.java. A.java will not compile this time since you have overwritten the CLASSPATH to C:\ and that's what sourcepath will default to as well. This is the output :

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

Also if the current directory is /top/wrk/pkg then the command javac A.java works. How so?

This will not work regardless of whether B.class is present in pkg

Disclaimer : I can only confirm this behavior on Windows but I highly doubt that it should be any different on other operating systems.

Chetan Kinger
  • 15,069
  • 6
  • 45
  • 82
  • Could you provide a link to the javac docs that specifies this behaviour? I couldn't find anything other than through specifying -sourcepath. – Phil Anderson May 29 '15 at 11:22
  • @PhilAnderson See my edit. Chances are that you have some value set in `CLASSPATH` so your `sourcepath` does not default to `.` – Chetan Kinger May 29 '15 at 11:54
  • Thanks Chetan, but I haven't got anything in the CLASSPATH. – Phil Anderson May 29 '15 at 12:01
  • @PhilAnderson Can you try compiling `A.java` using `javac -cp . pkg\A.java`? If it works, this confirms that `sourcepath` is defaulted to `CLASSPATH` – Chetan Kinger May 29 '15 at 12:05
  • As per your edit... You say that the sourcepath will be the same as the classpath. Where did you get that from? Why would the default be to look for source files in the same folder as your class files? – Phil Anderson May 29 '15 at 12:05
  • That was what I originally tried (with the -cp). It doesn't compile on Mac. – Phil Anderson May 29 '15 at 12:06
  • @PhilAnderson See my edit, when I override the `CLASSPATH` using `-cp` option, `A.java` stops compiling. This pretty much confirms that `sourcepath` uses `CLASSPATH` as it's default value. I highly doubt that it should work differently on a Mac or any other OS for that matter since both the documentation and the `verbose` output from `javac` cannot be wrong. I recommend that you take a closer look and make sure that you are not missing anything. – Chetan Kinger May 29 '15 at 12:22
  • And yet I've actually tried it on both Mac and Windows, and although I find it surprising, it IS different. I guess we'll agree to disagree on this. No harm done. – Phil Anderson May 29 '15 at 12:33
  • @PhilAnderson I don't disagree with you. I disagree that the documentation can be wrong. You can update your answer with your findings. – Chetan Kinger May 29 '15 at 12:44
1

The compiler has to either find and successfully compile a source for B, or find a .class for B even if it's just an import. As opposed to loading, which is done dynamically.

Look in your output directory and you'll see that B has been compiled as well. It will be compiled even if in a different package, but you'll have to make it public to reference it from A.

vempo
  • 3,093
  • 1
  • 14
  • 16
1

From the Oracle javac docs...

If you set the -sourcepath option, then the compiler searches the indicated path for source files. Otherwise, the compiler searches the user class path for both class files and source files. On Windows, the -sourcepath option seems to be set by default and your command works.

On my Mac though, it fails and gives following message...

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

To get it to automatically look and compile source files of dependencies, you would need to use the -sourcepath option. e.g...

javac -sourcepath ./* A.java
Phil Anderson
  • 3,146
  • 13
  • 24
  • See my answer. I believe the sourcepath defaults to `CLASSPATH` and chances are that you have `CLASSPATH` set on your machine. – Chetan Kinger May 29 '15 at 11:58