Java Reflection library - using Class.forName() without package info - java

I have a project which requires creating Classes dynamically at runtime, where the only information available is the Class name in String form. I'm testing right now with some dead simple Reflection library methods, but my question is, why do I have to provide a fully qualified Class name if the Classes I'm trying to 'dynamically' load are in the same directory? I'm using linux, no development environment, just all the Classes I'm trying to invoke in the same directory with no package declarations whatsoever. Do I have to set up a package system to use Reflection? Here's this little code snippet....
public class ReflectionTest {
public static void main(String[] args) {
try {
Class nodeClass = Class.forName("Node");
System.out.println(nodeClass.toString());
} catch (ClassNotFoundException cnfe) {
System.out.println("Error");
}
}
}
Now, this throws the error. But when I create an instance of Node, and then get it's fully qualified Class name with Node.getClass().getName(), I just get "Node" in return. So I don't understand why the Reflection library doesn't work the same as invoking the JVM with 'java', where if no package name is supplied it simply looks in the same directory. I'm not sure how to use this library with user-defined Classes when none of my Classes have an associated package name.

Your code should work as described. That is, if you can call new Node() you should also be able to call Class.forName("Node"). I've just tried this with a package-less class to verify and everything works as expected.
Is Node.class available in the classpath? You may get this to work by adding the current directory to the classpath.

The package name is an integral part of the java naming system. However, if your class does not declare a package, it is in the root package, the code you posted should work.
My guess: the directory is not actually in the CLASSPATH. Try adding the current directory (./) and making sure it is actually the current directory, or include the directory explicitly (absolute path) in your CLASSPATH. Refer to the docs to find out how to set CLASSPATH (environment, jvm options, runtime are your options)

It works for me using the exact ReflectionTest.java above and the following Node.java:
public class Node { }
Did you compile Node.java as well as ReflectionTest.java? Since the ReflectionTest class does not have a compile-time dependency on the Node class, javac won't automatically compile Node.class when it compiles ReflectionTest.class.

There is no concept of a directory for classes in Java. Classes are in a package, or are in the default package, and are loaded from directories or jars that must be in the classpath.
If the directory containing Node.class is in the classpath, the above code should work.
But it's a very very bad practice to use the default package. Every class should be in a package.

First of all you need to make sure that the classes that you're trying to load are in the classpath, so when launching the jvm try adding the current folder to it: java -cp . ReflectionTest
The concept of packages is to keep your stuff organized and to be able to reuse names. Providing the fully qualified name, ensures that you are using the correct Node class, and for example not org.w3c.dom.Nodeor something similar which would be implicitly shipped with java.

Related

Why do java source files require package declarations?

I think I am failing to understand java package structure, it seemed redundant to me that java files have a package declaration within, and then are also required to be present in a directory that matches the package name. For example, if I have a MyClass.java file:
package com.example;
public class MyClass {
public static void main(String[] args) {
System.out.println("Hello, World");
}
}
Then I would be required to have this file located in com/example, relative to the base directory, and I would execute java com.example.MyClass from the base directory to run it.
Why wouldn't the compiler be able to infer the package name by looking at the directory structure? For example, if I compiled the file from the base directory javac com\example\MyClass.java, I am not understanding why the MyClass.java wouldn't implicity belong to the com.example package.
I understand there is a default package, but it still seems that the package declaration in the source file is redundant information?
As you (implicitly) acknowledged, you are not required to declare the name of a package in the case of the default package. Let us put that quibble aside ...
The reason for this seeming redundancy is that without a package declaration, the meaning of Java1 source code would be ambiguous. For example, a source file whose pathname was "/home/steve/project/src/com/example/Main.java" could have 7 different fully qualified names, depending on how you compiled the code. Most likely, only one of those will be the "correct" one. But you wouldn't be able to tell which FQN is correct by looking at (just) the one source file.
It should also be noted that the Java language specification does not require you to organize the source code tree according to the packages. That is a requirement of a (large) family of Java compilers, but a conformant compiler could be written that did not require this. For example:
The source code could be held in a database.
The source code could be held in a file tree with random file names2.
In such eventualities, the package declaration would not be duplicative of file pathnames, or (necessarily) of anything. However, unless there was some redundancy, finding the correct source "file" for a class would be expensive for the compiler ... and problematic for the programmer.
Considerations like the above are the practical reason that most Java tool chains rely on file tree structure to locate source and compiled classes.
1 - By this, I mean hypothetical dialect of Java which didn't require package declarations.
2 - The compiler would need to scan the file tree to find all Java files, and parse them to work out which file defined which class. Possible, but not very practical.
Turn the question on its head:
Assume that the package statement is the important thing - It represents the namespace of the class and belongs in the class file.
So now the question is - Why do classes have to be in folders that match their package?
The answer is that it makes finding them much easier - it is just a good way to organize them.
Does that help?
You have to keep in mind that packages do not just indicate the folder structure. The folder structure is the convention Java adopted to match the package names, just like the convention that the class name must match the filename.
A package is required to disambiguate a class from other classes with the same name. For instance java.util.Date is different from java.sql.Date.
The package also gives access to methods or members which are package-private, to other classes in the same package.
You have to see it the other way round. The class has all the information about itself, the class name and the package name. Then when the program needs it, and the class is not loaded yet, the JVM knows where to look for it by looking at the folder structure that matches the package name and the class with the filename matching its class name.
In fact there's no such obligation at all.
Oracle JDKs javac (and I believe most other implementations too) will happily compile your HelloWorld class, no matter what directory it is in and what package you declare in the source file.
Where the directory structure comes into the picture is when you compile multiple source files that refer to each other. At this point the compiler must be able to look them up somehow. But all it has in the source code is the fully qualified name of the referred class (which may not even have been compiled yet).
At runtime the story is similar: when a class needs to be loaded, its fully qualified name is the starting point. Now the class loader's job is to find a .class file (or an entry in a ZIP file, or any other imaginable source) based on the FQN alone, and again the simplest thing in a hierarchical file system is to translate the package name into a directory structure.
The only difference is that at runtime your "standalone" class too has to be loaded by the VM, therefore it needs to be looked up, therefore it should be in the correct folder structure (because that's how the bootstrap class loader works).

Using classpaths

I plan on becoming a certified Java programmer and am studying from the Sierra-Bates book. I had a question about classpaths. Do classpaths need to find only the supporting classes of the class I'm running/compiling, or the supporting classes and the class itself? Also, when I'm getting classes in packages from classpaths, is it legal to just put the adress of the file(the path to it), instead of putting it's root package. Thanks.
1 - a classpath has to give access to each class that needs to run in your program. That would include the main class and any classes it calls and those they call. If there is some code in one of those classes that is never called, in many cases, you don't need to have the classes referenced by the uncalled code.
2 - you have to put the root of the packages in the classpath. So a class "com.bob.myprog.Main" would need to have the class path point to the folder where the "com" package/folder lies. It will need to contain a "bob" folder and "bob" will need to contain a "myprog" folder with "Main.class" in it.
Classpath has to contain both the supporting classes and the class itself.
However, sometimes you can run a single file without specifying classpath (and it will work).
As specified in http://docs.oracle.com/javase/tutorial/essential/environment/paths.html :
The default value of the class path is ".", meaning that only the
current directory is searched. Specifying either the CLASSPATH
variable or the -cp command line switch overrides this value.
Therefore, if you have a class MyClass compiled in the current directory, the following will work:
java MyClass
while pointing classpath to another directory will lead to an error (classpath no longer contains MyClass):
java -cp lib MyClass
When you have a class in a package, it is not enough to put the address to the class file in the classpath. According to SCJP Sun Certified Programmer for Java 5 Study Guide:
In order to find a class in a package, you have to have a directory in
your classpath that has the package's leftmost entry (the package's
"root") as a subdirectory.

How do I reference classes and methods in JAR file inside Eclipse?

Basically I wish to use the methods of a class within the Jar file, which looks like this:
Can somebody please tell me what I need to import in order to use those methods and variables?
You don't need to import anything.
Jar files aren't imported, they are added to the classpath.
From the screenshot you've posted, we can see that the myJar.jar file is included in your eclipse classpath, so there's nothing more to do there.
Classes are imported, if they are in a different package.
Your Examplew class is in the default package. BMIcalculator is also in the default package. Because they are the same package, you don't need to import it.
You should be able to simply make references to BMIcalculator from within Examplew. Just try it.
Try compiling this code - it should work:
public class Examplew
{
private BMIcalculator calc = new BMIcalculator();
}
You might get warnings about the unused private field, but you can ignore that for now.
If that doesn't work for you, then please post the error, because it doesn't look like the problem is with your imports (or your classpath)
Quote from this question:
You can’t use classes in the default package from a named package.
Prior to J2SE 1.4 you could import classes from the default package using a syntax like this:
import Unfinished;
That's no longer allowed. So to access a default package class from within a packaged class requires moving the default package class into a package of its own.
If you have access to the source generated by groovy, some post-processing is needed to move the file into a dedicated package and add this "package" directive at its beginning.

How to make the java ServiceLoader work in a NetBeans 6.9 module application

I have troubles using the java ServiceLoader in a NetBeans module application. Here is what I'm trying to do (and it works in a normal java application in Eclipse):
I have an interface.jar, which declares the interface. And I have implementations.jar, which has several implementations of this interface, all specified in the spi/META-INF/services/my.package.name.MyInteface file (this file is in the implemenations.jar).
I also have a class ImplementationHandler (in yet another handler.jar), which has the following method to load all implementations:
private static List<MyInterface<?>> loadAllImplementations() {
List<MyInterface<?>> foundImplementations = Lists.newArrayList();
try {
for (MyInterface implementation : ServiceLoader.load(MyInterface.class)) {
foundImplementations.add(implementation);
}
} catch (ServiceConfigurationError error) {
system.out.println("Exception happened");
}
return foundImplementations;
}
This code returns all implementations in Eclipse normal application (the foundImplementations.size() > 0).
However under NetBeans, it can't find anything (foundImplementations.size() == 0).
More details:
I have the source of a NetBeans module application (open source, not written by me), which I need to extend by using some of MyInterface implementations. The interface.jar, implementations.jar and the handler.jar are created in Eclipse and they are part of another application.
In the NetBeans, I opened the module which needs to use the new impplementations and I added all my 3 jars as external libraries (NetBeans copied them into its ext folder, which I don't want but I can't do anything about - I want them in another myext folder, but that's another story). Then I rebuilt everything and tried to use one of my implementations, but it was not found... The code that gets an implementation is in the ImplementationHandler class and looks like:
public static final <T> MyInteface<T> getByName(String name) {
for (MyInteface implementation : loadAllImplementations()) {
if (implementation.getName().equals(name)) {
return implementation;
}
}
throw new IllegalArgumentException("Unable to find MyInterface class for: " + name);
}
I got my exception "Unable to find MyInteface class for: myImplementationName"...
My knowledge about NetBeans is very limited and I was wondering is there something more that I need to do in order to get this working?
In order to work, you have to make Netbeans create this services sub folder inside META-INF. It's very easy to do, but the information is easily accessible.
To add something to META-INF, you need to create a folder of this name in your src/ (the source directory [spi?]) folder. In this case you also need the services folder and in it, create a text file with the same fully qualified name as your service interface. In the end you should have this structure: src/META-INF/services/my.package.MyInterface.
Finally, this [my.package.MyInterface] file's content should list all the implementation classes (one per line).
With this setup, Netbeans will create the appropriate jar when building your app.
Take a look at this ServiceLoader example. It's a complete example, although it does not explain the Netbeans integration I just described.
The ServiceLoader.load(Class) uses the current thread's context class loader to load all the implementations. It may be that in your case your implementation classes in your jar file (implementation.jar) are not in that class loader's classpath.
You may have to try different approaches for this :
You may either need to have all the jars in the netbeans module's classpath or,
You may need to create a class loader (probably a URLClassLoader having those jars in its classpath) and use the ServiceLoader.load(Class, ClassLoader) and pass a that classloader.
There is another option you could try but I am not sure about this: The jar file spec allows you to specify Class-Path manifest attribute, to which you can add 'implementation.jar' entry. More details here
Most likely, the handler.jar and implementations.jar are not loaded by the same class loader. Also you may want to take a look as to why your files are getting to ext folder.
Hope this helps.
Edit:
Also try calling the ServiceLoader.load(Class, null) which uses the System class loader (the one that started the application). Probably that classloader may be able to find classes in jars located in the ext directory.
I gave up, and replaced ClassLoader with JSPF. This works out of the box and I don't need to know about the internals of a third party program, written as NetBeans module and how this affects the classpath given to the class loaders.

How to use jar files without package information?

I have a jar called "MyTools". The jar is in c:\data folder. I created a new file in the same folder called "UseTools.java". Now I would like to use some of the classes from the MyTools.jar in my UseTools.java. I tried this but it doesnt seem to work:
import MyTools.*;
public class UseTools
{
public static void main(String[] args)
{
MyTools.SomeClass foo = new SomeClass();
SomeClass.doSomething();
}
}
I tried to compile this with:
javac -cp . UseTools.java
and got this error message:
UseTools.java:1: package MyTools does not exist
import MyTools.*;
^
UseTools.java:7: package MyTools does not exist
MyTools.SomeClass foo = new SomeClass()
^
2 errors
I did not set the package name in any class.
Do I have to set a package name in my jar classes?
To mention something that relates more to the title of the question:
In Java, you can't access classes in the default package from code within a named package.
This means, if the classes in your jar file do not belong explicitly to any package and inside the jar your files are directly in the root folder without subfolders, they are in the default package. This is not very elaborated and lacks modularity as well as extensibility, but is technically alright.
Then, you can only use these classes from code which also is in the default package. But this does not necessarily mean it has to be in the same jar. If you have multiple src or class folders they could all contain classes in the default package which can interact. The organization in JAR files and the package structure in your project are independent of each other.
However, I'd strictly encourage you to use explicit package information.
In your MyTools.jar there should be a package with the name MyTools. And before compiling you should add the jar to the classpath.
You need to add -cp file.jar instead of -cp .
The latter one will pick up .class files only. BTW: why not using an IDE like netbeans, eclipse or intelliJ?

Categories

Resources