Tell javac to read class/interface dependency from classfile - java

I'm experimenting with java to see if I can build a plugin system into my application.
The one rule I want the plugins to adhere to is that they need to implement interface Cake.
My directory is:
Projectfolder
bin
Foo.class
Cake.class
src
Foo.java
Bar.java
Cake.java
Cake.java and Foo.java is in package test.package and Bar.java is in lets say plugin.stuff package (since I don't control this it could of course be anything).
Foo.java and Cake.java compiles to JBC (JavaByteCode) without a problem.
Since I don't want to ship my application code to everyone interested in creating a plugin I want to read the information about the interface Cake (which is implemented in Bar.java) from it's .class file. Is this possible?
What javac commands is needed to tell javac to take dependency Cake from Cake.class?
To clarify I want to be able to go from:
Foo.class
Cake.class
Bar.java
to:
Foo.class
Cake.class
Bar.class

Okay I found the error or at least I got it working.
Since the file Foo.class and Cake.class wasn't placed in folder src/test/package/ the classes/interfaces was not found.
When I created this hierarchy:
Projectfolder
bin
test
package
Foo.class
Cake.class
src
Bar.java
and in the bin folder ran the command javac ..\src\Bar.java then the command ran fine and Bar.class file was generated in the src folder next to the Bar.java file.

Related

How do you specify the classpath when running the javac command?

I'm trying to import Bar.java into Foo.java, where my directory is structured like this:
.
└─bar
└─Bar.java
└─foo
└─Foo.java
So I create Bar.java:
package bar;
public class Bar {}
and Foo.java:
import bar.Bar;
public class Foo {}
I run javac from the foo directory:
javac Foo.java -cp ../bar
and receive the error:
Foo.java:1: error: package bar does not exist
My understanding was that the -cp option should be used to point towards the required class (Bar.java). Could someone shed some light on what I'm doing wrong?
Package bar does not exist because the prompt or terminal "working directory" is inside foo.
Easiest, make a folder java2 and put both folders in it and use use the storage folder as "system working directory" for terminal or prompt,
However, normal "package" hierarchies compiled together are both in a top level starting directory tree.
E.g.
/com/foo/ and /com/bar
So your package would look com.foo and com.bar
Put the com folder in the java2(simply a name you can use any name for the storage folder)folder.
Your import statement at the top of the class files should have the com directory added to import the other file.
Just command inside java2 folder
javac /com/foo/Foo.java
-cp is for specifying to include dependent .jar libraries for the compilation.
If Foo does not import Bar then it will be required to be separately compiled the same way as Foo.
When framework API libraries are made its the user program requires to mark a starting point to unload the jar or look for a package folder and is traditionally "com", however, it does not distinguish which API until a folder lower so your package folder really should be structured
/com/mypersonalapi/foo
/com/mypersonalapi/bar
com.mypersonalapi.foo
com.mypersonalapi.bar
You need to give like this:
javac Foo.java -cp ..
When you give classpath as shown in your question, it expects the package "bar" under the folder "bar"; which is not intended.

java compiling from the command line

I am an experienced programmer, but haven't used Java in years - mostly C# - and used an IDE in the past. I'm trying to compile some code from the command line on my Mac, but can't get my test file to find my source code. I'm assuming the problem lies somewhere in the space of packages, file structure, classpaths, and import statements - but I've put a couple hours in (including hunting on Stack Overflow) and am still stuck.
Here's what I have:
Directory structure:
ProjectName
|
--src
|
--SourceClass
--test
|
--SourceClassTest
--external
|
--testng-6.8.7.jar
My SourceClass looks like this:
package ProjectName;
public class SourceClass<T>{
}
Very simple. Obviously, there will be more - but I wanted to start with making sure I had all this setup stuff correct before I actually did coding.
My test class looks like this:
package ProjectName;
import java.util.*;
import org.testng.Assert;
import org.testng.annotations.*;
public class SourceClassTest{
#Test
private void createEmptySourceClass(){
SourceClass<Object> sourceClass = new SourceClass<Object>();
Assert.assertTrue(sourceClass.isEmtpy());
}
}
The sourceClass compiles with no issue with "javac src/*.java", run from the "ProjectName" directory. I want to see this fail with an error along the lines of "SourceClass doesn't have an isEmpty() method", but instead I run javac like this from the "ProjectName" directory:
javac test/*.java -classpath external/testng-6.8.7.jar
and get this exception:
test/SourceClassTest.java:12: error: cannot find symbol
SourceClass<Object> tree = new SourceClass<Object>();
^
symbol: class SourceClass
location: class SourceClassTest
test/SourceClassTest.java:12: error: cannot find symbol
SourceClass<Object> sourceClass = new SourceClass<Object>();
^
symbol: class SourceClass
location: class SourceClassTest
2 errors
I've tried a lot of things -adding an import statement, adding a sourcepath to the javac command, compiing the sourceClass as a jar and putting it in the bin directory then adding that to the classpath, but I can't get the test to find the SourceClass symbols.
Any idea what I am missing here?
It works if you compile into a separate target directory. E.g,
mkdir target
javac -d target/ src/*.java
javac -classpath target/ test/*.java
When you do javac src/*.java, it will create the .class file in the src directory itself. By default, any classes you reference are assumed to be in the same package. So even if you add src/ to the classpath, it looks for src/ProjectName/SourceClass.class, which it does not find. When you pass the -d target/ option, it creates the proper package hierarchy, so and finds the class.
Relevant documentation from the javac official doc:
You should arrange source files in a directory tree that reflects
their package tree. For example, if you keep all your source files in
C:\workspace, the source code for com.mysoft.mypack.MyClass should be
in C:\workspace\com\mysoft\mypack\MyClass.java.
By default, the compiler puts each class file in the same directory as
its source file. You can specify a separate destination directory with
-d (see Options, below).
...
...
-d directory Set the destination directory for class files. The directory must already exist; javac will not create it. If a class is
part of a package, javac puts the class file in a subdirectory
reflecting the package name, creating directories as needed. For
example, if you specify -d C:\myclasses and the class is called
com.mypackage.MyClass, then the class file is called
C:\myclasses\com\mypackage\MyClass.class. If -d is not specified,
javac puts each class files in the same directory as the source file
from which it was generated.
Note: The directory specified by -d is not automatically added to your
user class path.
My guess is it can't find SourceClass because the file defining that class is under src, and you didn't mention that directory in your javac command line.
If I were you, I would change the file hierarchy to this:
ProjectName/src/ProjectName/SourceClass.java
ProjectName/src/ProjectName/SourceClassTest.java
ProjectName/external/testng-6.8.7.jar
Then run javac src/ProjectName/*.java -classpath external/testng-6.8.7.jar.
Or keep the file hierarchy the way it is, and run javac src/*.java test/*.java -classpath external/testng-6.8.7.jar
The accepted answer is correct, but it misses one critical point: when javac is asked to compile *.java (as opposed to foo.java, and then foo2.java ...) it treats them as a single package and accepts references between them.
That's the magic. Other languages do this less implicitly with header files.
Even after reading this post, it took me some time to figure that out, against my inherent assumption that a program running singly on files one after the other would (should) produce the same result as running that program on a group of files. My bad; the * is NOT a mere convenience, but critical.

javac : Compiling a .java file which uses other classes in it

HI i have 3 java files
a.java
b.java
c.java
I managed to generate .class files for both a and b using
javac example/a.java
javac example/b.java
but when i do the same for c.java I get the error
error: cannot find symbol b and c
Any suggestions on how i could solve this problem ?
All the java files are in the same folder
You have to have classes a and b in your classpath when you try to compile class c. This allows the compiler to verify that they exist, figure out what methods they have, etc.
javac is pretty sensitive to package names and classpaths. The easiest thing to do is to compile all three at the same time like so javac example/a.java example/b.java example/c.java.
If you go to the parent directory of example (let's call it src), then you can run the following:
javac -cp src src/example/c.java
The reason you have to do it this way is because your classes have their packages listed as example. Because of your package name, javac is looking for the example directory in its classpath, where it expects to find a.class and b.class.
Presumably you're not in the example/ directory when you run javac. Try
javac -cp example c.java
Or just cd into that directory. The classpath is not automatically resolved for the classes c.java depends on.

How do I compile a Java class which uses another Java class as an Object?

I have an A.java class which uses B.java class as an object.
When I compile A.java class, it throws a compile error message, since the Java compiler can not reference the B.java object at all. So, here is my question:
How do I compile A.java class if it includes another B.java class?
Eclipse is a great tool, but this tool is not useful when I need to compile a Java file for Java beans.
You haven't explained how you're trying to compile A.java, or whether you've already compiled B.java. If you haven't compiled either of them yet, just compile them both together, e.g.
javac -d bin path/to/A.java path/to/B.java
If you've already compiled B, you need to make sure you've got the classpath right, e.g.
javac -d bin -cp path/to/Broot path/to/A.java
Note that the classpath value shouldn't be the B.class file itself, nor even the directory containing B.class - but the root of the output hierarchy. So if B is in package foo.bar, and B.class is in directory /x/y/z/foo/bar you'd write:
javac -d bin -cp /x/y/z path/to/A.java
make sure that the dependency(jar file) and the current directory is in the classpath when you compile a class.
This is a duplicate of the question: Can I use JAVAC to compile a project with multiple files and directories?
Depending on the type of object B you must add it to the classpath. If it's in a jar file (library) you should the -c option on command line. For adding other source files use the -s option.
Once you start bigger projects it will be hard to keep doing this manually on the command line. Look at a proper IDE like eclipse. It does all that for you. You only have to set the classpath in the properties once and then you can just compile with the click of some menu options (or set automatic building or use shortcut keys).
The error seems to be that the class B is not yet compiled. Could you check class B for errors, compile B, and then go for A?

Do not understand following use of -classpath by javac

I am not very clear with the following question from SCJP Book (I read the solution and explanation though) ..
Consider the following directory structure :-
foo --> test --> xcom --> A.class, B.java
Here foo, test and xcom are directories. A.class and B.java are the files in xcom directory.
Following are the source codes of corresponding files:-
A.java
package xcom;
public class A { }
B.java
package xcom;
public class B extends A { }
The default classpath is /foo.
Now, in order to compile B.java, I keep my current directory as test and give :-
javac -classpath xcom xcom/B.java
Here I give the classpath as xcom which has A.class. But still it does not find class A. Why is it so??
If your classes are in package xcom, then your classpath needs to be at the directory directly above that. In this case, the classpath should be foo/test.
And if your current directory is foo/test, then this should be your javac:
javac -classpath . xcom/B.java
Because you have to specify classpath root to -classpath argument, like javac -classpath . xcom/B.java. To compile class B java compiler requires class A, it tries to locate class A file in {classpathroot}/xcom/.
Note: . - is a current directory
I think the root cause here is a misunderstanding of a "fully-qualified name" in Java.
The fully-qualified names of your two classes are xcom.A and xcom.B. Their source is in files A.java and B.java in a directory named xcom; the fully-qualified names dictate the directory structure. When you are going to use the files, either to compile them or run them, the classpath contains one or more locations from which the fully-qualified names can be found; so java is looking for xcom\A.java and xcom\B.java (when compiling) and xcom\A.class and xcom\B.class (when running).
That is why the classpath needs to specify the directory that contains xcom.
As you progress to more complex environments: the classpath can be a list of such locations; each location is separated by a semicolon on windows and a colon on unix systems. Each location can be a directory, as you've already seen, but it can also be a jar file. jar files are in zip file format, and zip files have a directory structure just like disks do. So you could zip up your class files, maintaining their xcom parent (but not their full paths), and specify the jar file in the classpath instead of a directory.
I know the question was already answered somewhat, but thought you might like the background explanation as well.

Categories

Resources