Consider the following two Java classes:
a.) class Test { void foo(Object foobar) { } }
b.) class Test { void foo(pkg.not.in.classpath.FooBar foobar) { } }
Java by design does compile-time depenency checking and uses it not only to determine types but to determine method calls when they are overloaded. I know of no way around that.
What can be done (and is done for, say, JDBC drivers) is to delay dependency checking through the use of reflection. You can get the class from Class.forName
without the compiler knowing the class at compile time. In general, however, this means that the code is written to an interface and at runtime a class is loaded that implements the interface.
I can't see how you could allow this without breaking java type checking. How would you use your referenced object in your method? To extend on your example,
class test {
void foo (pkg.not.in.classpath.FooBar foobar) {
foobar.foobarMethod(); //what does the compiler do here?
}
}
If you're in some situation where you've got to compile (and call a method ) on something that works on a library you don't have access to the closest you can come is to get the method via reflection, something like (method calls from memory, may be inaccurate)
void foo(Object suspectedFoobar)
{
try{
Method m = suspectedFoobar.getClass().getMethod("foobarMethod");
m.invoke(suspectedFoobar);
}
...
}
I can't really see the point of doing this, though. Can you provide more information on the problem you're trying to solve?
About the only thing you can do is use some bytecode manipulation to transform it to the more specific type.
There is nothing in the Java grammar for a use of pkg.not.in.classpath.FooBar
to distinguish this:
package pkg.not.in.classpath;
public class FooBar { }
from this:
package pkg.not.in.classpath;
class FooBar { }
So there's only your word that it's legal to use FooBar there.
There also an ambiguity between package scoped classes and inner classes in source:
class pkg {
static class not {
static class in {
static class classpath {
static class FooBar {}
}
}
}
}
The inner class is also called pkg.not.in.classpath.FooBar
in the source but will be referred to as pkg$not$in$classpath$FooBar
rather than pkg/not/in/classpath/FooBar
in the class file. There is no way that javac can tell which you mean without looking for it in the classpath.
I created two classes : Caller
and Callee
public class Caller {
public void doSomething( Callee callee) {
callee.doSomething();
}
public void doSame(Callee callee) {
callee.doSomething();
}
public void doSomethingElse(Callee callee) {
callee.doSomethingElse();
}
}
public class Callee {
public void doSomething() {
}
public void doSomethingElse() {
}
}
I compiled these classes and then disassembled them with javap -c Callee > Callee.bc
and javap -c Caller > Caller.bc
. This produced the following:
Compiled from "Caller.java"
public class Caller extends java.lang.Object{
public Caller();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public void doSomething(Callee);
Code:
0: aload_1
1: invokevirtual #2; //Method Callee.doSomething:()V
4: return
public void doSame(Callee);
Code:
0: aload_1
1: invokevirtual #2; //Method Callee.doSomething:()V
4: return
public void doSomethingElse(Callee);
Code:
0: aload_1
1: invokevirtual #3; //Method Callee.doSomethingElse:()V
4: return
}
Compiled from "Callee.java"
public class Callee extends java.lang.Object{
public Callee();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public void doSomething();
Code:
0: return
public void doSomethingElse();
Code:
0: return
}
The compiler generated a method signature and a typesafe invokevirtual
call for the method calls to 'callee' - it knows what class and what method is being invoked here. If that class wasn't available, how would the compiler generate the method signature or the `invokevirtual'?
There is a JSR (JSR 292) to add an 'invokedynamic' opcode that would support dynamic invocation, however this isn't currently supported by the JVM.
Extract interface
pkg.in.classpath.IFooBar
make the FooBar implements IFooBar
and
class Test { void foo(pkg.in.classpath.IFooBar foobar) {} }
Your Test class will get compiled. Just plug the right implementation i.e. FooBar
in the runtime using factories and configuration. Look for some IOC containers.
I don't think there is such a way - the compiler needs to know about the class of the argument, in order to create appropriate bytecode. If it cannot locate the Foobar class, it cannot compile the Test
class.
Note that while your two classes are functionally equivalent since you're not really using the argument, they aren't identical and will yield different bytecode when compiled.
So your premise - that the compiler doesn't need to find the class to compile in this case - is incorrect.
Edit - your comment seems to be asking "can't the compiler just overlook the fact and generate the bytecode that would be appropriate anyway?"
The answer is that no - it can't. According to the Java Language Specification, method signatures must take types, which are elsewhere defined to be resolvable at compile-time.
Which means that while it would be mechanically quite simple to create a compiler that would do what you're asking for, it would violate the JLS and thus wouldn't technically be a Java compiler. Besides, circumventing compile-time safety doesn't sound like a great selling-point to me... :-)