Guys, can anyone explain the following scenario:
1) Web application has module1.jar in its lib directory. There is a class A in that module:
package module1;
import module2.B;
public interface IA {
void methodOk() {}
void methodWithB(B param) {}
}
package module1;
import module2.B;
public class A implements IA {
public A() {}
//...
void methodWithB(B param) {
//do job on B
}
}
2) module2.jar is absent - it is not in the classpath.
3) Application is able to create objects of class A though it's missing the dependency. In application a method A.methodOk() is called.
Would be cool if you could give a reference to any spec on this.
Thanks a lot.
Since the code is already compiled, it will not throw an error until you directly use class B. From the looks of your code, you don't actually use an instance of B for anything.
If B is not used by A anywhere, then the resulting bytecode will have no reference to module2.B, therefore it gets compiled away. No dependency exists, except at compilation in this case.
If the question is unclear and B is used in A somewhere, then I'd be interested in seeing more code to try to determine what's going on.
Look at it from the perspective of the classloader. If you never have to load the class, you don't care if the bytecode for that class is missing.
Your question is really, "What triggers classloading?"
Two reasons I can think of off the top of my head are:
- Construction
- Static access
Related
Consider the following scenario:
Let say I have a class A in "src" folder of my project.
class A {
void foo() {
B b = new B();
}
}
Class B is defined in another jar which is included as a dependency in build.gradle
class B extends C {
}
Now, Class C is defined in another jar which will be provided on runtime and not on compile time. Gradle is able to compile Class A without error.
But, when I import Class c in Class A then it gives "class not found".
import other.C; // this line gives error
class A {
void foo() {
B b = new B();
}
}
Is this the desired behavior of Java compiler to ignore the Class C if it not imported directly?
Also, what happened if use a function in class A using object of B which is in Class C but not overridden in class B.
The exact answer to your question depends on the Java compiler version and whether or not it requires access to C for doing its job.
All in all, I would say that such a setup is fragile and you should not do it. If your library that defines A requires B which effectively makes use of C in its public API as is the case for extends then C should be made visible to your library.
First of all we need to understand how java compilers work.
Whatever you reference by name or as a 'token' in your code, should be accessible to compiler during compilation phase.
The classes that you load using Class.forload or getClass method are not required to be available in the classpath.
What you are essentially referring by compiletime and runtime is about packaging.
What you say that a particular dependency say Class C will be provided at runtime, its an instruction to bundle tasks to ignore that dependency while building the jar. So if you compile and deploy your application as jar, class C will not be present in it. At the same time, the jar containing class B will be included in your deployment package.
If you provide exact gradle file, I might be able to answer more precise.
I am trying to make a Java app that can load plugins implementing an abstract class and am having an AbstractMethodError with the instances generated from ServiceLoader. The code is a bit heavy so I've made a simplification below.
First, I have an abstract class:
package stuff.TheAbstractClass;
public abstract class TheAbstractClass implements ClassInterface{
//Stuff happens
}
Which implements the below interface:
package stuff.ClassInterface;
public interface ClassInterface {
public String getClassName();
}
I have a service provider NotAbstractClass which extends TheAbstractClass and states so in a meta-inf/services folder:
package anotherstuff.NotAbstractClass;
public final class NotAbstractClass extends TheAbstractClass implements ClassInterface{
private String name = "Silent Bob";
#Override
public String getClassName() { return name; }
}
Then on the main application (which is actually a plugin inside another application), I want to find all classes which extend TheAbstractClass:
package stuff.TheApp;
import java.util.ServiceLoader;
public class TheApp {
private String name;
public final static TheApp INSTANCE = new TheApp();
private TheApp() {
ServiceLoader<TheAbstractClass> serviceLoader =
ServiceLoader.load(TheAbstractClass.class);
for (TheAbstractClass class: serviceLoader) {
name = class.getClassName;
}
}
My application does find NotAbstractClass. I know this since, in the for loop, I can do class.getName() and it'll give me anotherstuff.NotAbstractClass) but gives the error:
java.lang.AbstractMethodError: stuff.TheAbstractClass.getClassName()Ljava/lang/String;
I'm stumped. Any suggestion? Thank you, Pedro
According to the API for AbstractMethodError you get this:
Thrown when an application tries to call an abstract method. Normally,
this error is caught by the compiler; this error can only occur at run
time if the definition of some class has incompatibly changed since
the currently executing method was last compiled.
Just by looking at your code and your comment I see that this could only have happened at runtime.
If that is the case then:
some class has incompatibly changed since the currently executing
method was last compiled
I've tested your logic after some adjustments in a Java compatible form and I had no problems. The only thing that seems to be happening is a change in any of the subclasses of TheAbstractClass.
Another thing I did was to declare the dependencies using the dependency files in: resources/META-INF/services:
file: <full-package>.TheAbstractClass
content: <full-package>.NotAbstractClass
After this I had no problems.
It seems the issue wasn't in the code, but in the IDE (IntelliJ). I deleted all previously packaged jars and made new jars without changing anything and it magically worked... So it's an IDE bug, and not a language issue!
Thanks to #Joao and #hotzst for taking time to read however.
Best, Pedro
I have a JAR file that I'm using and I want to modify one of the files inside it. In a nutshell I have
public class ClassB {
public void printMethod(){
ClassA A = new ClassA();
A.printout();
}
}
public class ClassA {
public void printout(){
System.out.println("1234");
}
}
and I want to change ClassA's printout method to
public class ClassA {
public void printout(){
System.out.println("abcd");
}
}
I know you cannot modify a JAR without unpacking/rebuilding it and for this, let's say I can't do that. Is there a way to make modifications to ClassA without actually touching the current ClassA? My approach is to have a new class inherit from ClassA with an overridden method and then have a new class inherit from ClassB that calls the Inherited ClassA
public class InheritedClassA extends ClassA{
#Override
public void printout(){
System.out.println("abcd");
}
}
public class InheritedClassB extends ClassB{
#Override
public void printMethod(){
InheritedClassA A = new InheritedClassA();
A.printout();
}
}
I don't like this approach though because in my actual JAR, so many classes are using ClassA that its a nightmare trying to correctly do this with all of them, which then all require the same process on them. I know you cannot overload/overwrite a whole class which is basically what I want to do. Is there another way to do this?
EDIT
To make it even harder, I cannot download any new frameworks or software or anything.
I can only provide pointers as I never felt the need for it.
What you are referring to is called "Bytecode enhancement", and yes there are several frameworks to achieve it.
BCEL - http://commons.apache.org/bcel/
ASM - http://asm.ow2.org/
Usually, java developers prefer to use the inversion of control pattern. This allows the code to configure itself at runtime via a configuration file - See Spring IoC for more details.
One option which may not be feasible would be to create a new version of ClassA, package it up in its own jar file, and put that ahead of the original version in your classpath.
However, this is a pretty odd scenario - why can you not update the existing jar file? Even if that means a bit of extra work, it's likely to be much cleaner in the long run than any other approach.
i'm very confused...
I have a class which directly implements an interface:
public class Device implements AutocompleteResult
{...}
Here is proof that I'm looking at the right variables:
Object match = ...;
log.debug(match.getClass()); // Outputs 'Device'
log.debug(match.getClass().getInterfaces()[0]); // Outputs 'AutocompleteResult'
Yet when I try to cast an instance of the class to the interface:
AutocompleteResult result = (AutocompleteResult) match;
I get a ClassCastException!
ClassCastException: Device cannot be cast to AutocompleteResult
Also, isAssignableFrom returns false and i'm not sure why:
log.debug(AutocompleteResult.class.isAssignableFrom(Device.class));
from the doc:
Determines if the class or interface represented by this Class object is either the same as, or is a superclass or superinterface of, the class or interface represented by the specified Class parameter.
Shouldn't I always be able to cast a object to an interface its class implements?
Thanks.
This can happen if two different classloaders load a class named AutocompleteResult.
These two classes are then treated as entirely different classes, even if they have the same package and name (and even implementation/fields/methods).
A common cause for this is if you use some kind of plugin system and both your base classes and the plugin classes provide the same class.
To check for this issue print the value returned by Class.getClassLoader() on both offending classes (i.e. the class of the interface implemented by Device and the result of AutocompleteResult.class).
AKA when Java apparently doesn't Java.
I hit this problem recently with Play Framework 2.6.3, what helped me was this:
https://www.playframework.com/documentation/2.6.x/ThreadPools#Application-class-loader
I leave this info here for the people that might have the same problem.
To make it clearer, what helps is:
Injecting Application on an Eager Singleton and then using its classloader to load the classes I was having issues with.
To make it clearer
public class Module {
#Override
public void configure {
bind(TheClassLoaderLoader.class).asEagerSingleton()
public static class TheClassLoaderLoader {
#Inject
public TheClassLoaderLoader( Application application) {
ClassLoader classloader = application.classloader();
Class<?> interfaceClass = classloader.loadClass(InterfaceClass.class.getName());
classloader.loadClass(ImplementsInterfaceClass.class.getName()).asSubclass(interfaceClass);
The example here https://playframework.com/documentation/2.6.x/JavaDependencyInjection#Configurable-bindings
That uses Environment often throws a frustrating ClassNotFoundException
Cheers
I have three .java files and I need to get them to work together. I think that I need to add all the classes to a main method but I am not sure if this is correct and if I just add the name of the class and the format.
I figured it out, the three files had a package listed at the top of each. I created a new Java project in Eclipse and then a source folder and in the source folder I created a package with the name that they all referenced. Now it runs. Thanks for all of you help for the Eclipse/Java beginner.
You are right: what you think is not right :P
Java can find the classes that you need, you can just use them straight away. I get the feeling that you come from a C/C++ background (like me) and hence think that you will need to "include" the other classes.
java uses the concept of namespaces and classpaths to find classes. Google around for it.
A little example of how variety of classes can be used together:
// A.java
public class A {
public void sayIt() { sysout("Said it by A!"); }
}
// B.java
public class B {
public void doIt() { sysout("Done it by B!"); }
}
// MainClass.java
public class MainClass {
public static void main(String[] args) {
A aObj = new A();
B bObj = new B();
aObj.sayIt();
bObj.doIt();
}
}
Note that there are no includes/imports here because all of the classes are in the same namespace. If they were not, then you'd need to import them. I will not add a contrived example for that coz its too much to type, but should google for it. Info should be easy enough to find.
Cheers,
jrh
If they are in the same package you do not need to do anything, as they are automatically imported for you, but otherwise you'll need to add import statements before your class declaration.
Once this is done, you can reference static members directly ie ClassB.staticMethod(); or instantiate the class ie ClassB classb = new ClassB();
But honestly, if you are this confused, you need to spend some more time doing tuturials.
http://eclipsetutorial.sourceforge.net/totalbeginner.html
http://download.oracle.com/javase/tutorial/getStarted/cupojava/index.html
http://www.freejavaguide.com/corejava.htm
I am not sure what you mean by "adding classes to a main method". If you want to make use of several classes inside your Java program, just import the needed classes/packages at the beginning and create an instance of each class as you go along.
I learned this from a beginner program called Jeroo
Basically if I want to create a new "Jeroo", I would write the following on my Main method:
Jeroo Bob = new Jeroo();
{ methods... }
So basically:
[class] [customnameofclass] = new [class]