Hello everyone,
I stumbled upon a java compilation puzzle that I would like to discuss
with you.
Let's say I have two classes Foo and Bar defined as:
public class Foo {
}
public class Bar {
public Bar(String s) {}
public Bar(Foo f) {}
}
I can compile Foo first and then I can compile Bar with Foo in javac's
classpath. So far, so good.
Let's say now that I have the following Main class:
public class Main {
public static String main(String[] args) {
for (int i = 0; i < args.length; i++) {
Bar b = new Bar(args[i]);
}
}
}
That code does not mention the class Foo yet it will not compile if
Foo is not in the classpath. The error I get is:
src/Main.java:5: cannot access Foo
file Foo.class not found
Bar b = new Bar(args[i]);
^
1 error
I can understand that javac needs to disambiguate the two constructors
of Bar to compile the Main class. But I do not understand why it needs
to actually have the Foo class for that. It knows String and knows
that it does not implement or extend Foo. So there is no ambiguity in
the call to the String-based constructor: it can't be the
Foo-based one.
I do not bring this issue just for argument's sake. I am in the
situation where I have a jar containing Bar and Foo is in a second jar
that I don't have. My code (Main) would not compile without Foo so I
had to create a fake Foo class just to compile successfully. It seems
kind of silly. Or did I miss something?
Cheers,
Thomas
conrad@lewscanon.com - 01 Jul 2008 20:29 GMT
> Hello everyone,
>
[quoted text clipped - 28 lines]
> That code does not mention the class Foo yet it will not compile if
> Foo is not in the classpath.
That code does not, but it does mention Bar, and Bar in turn does
mention Foo, so in order to resolve Bar, Foo must be in the classpath.
> The error I get is:
> src/Main.java:5: cannot access Foo
[quoted text clipped - 9 lines]
> the call to the String-based constructor: it can't be the
> Foo-based one.
If a class is referenced then it must be available. Bar references
Foo, so Foo must be in the classpath. The fact that Main does not
reference Foo is beside the point. Bar is referenced, so anything
that Bar in turn also references must also be.
It is not enough that Main doesn't reference Foo explicitly - the
reference chain is transitive so Bar pulls Foo in for you. It's a
matter of completeness. The definition of Bar is incomplete without
Foo. Likewise the definition of Main is incomplete with Bar. So Main
has to have Foo.
--
Lew
Roedy Green - 02 Jul 2008 01:50 GMT
On Tue, 1 Jul 2008 09:24:15 -0700 (PDT), Lethal Possum
<lethal.possum@gmail.com> wrote, quoted or indirectly quoted someone
who said :
>I do not bring this issue just for argument's sake. I am in the
>situation where I have a jar containing Bar and Foo is in a second jar
>that I don't have. My code (Main) would not compile without Foo so I
>had to create a fake Foo class just to compile successfully. It seems
>kind of silly. Or did I miss something?
see http://mindprod.com/jgloss/circularreference.html

Signature
Roedy Green Canadian Mind Products
The Java Glossary
http://mindprod.com
Lew - 02 Jul 2008 02:19 GMT
> On Tue, 1 Jul 2008 09:24:15 -0700 (PDT), Lethal Possum
> <lethal.possum@gmail.com> wrote, quoted or indirectly quoted someone
[quoted text clipped - 7 lines]
>
> see http://mindprod.com/jgloss/circularreference.html
The OP's scenario is not an example of a circular reference.

Signature
Lew
Lethal Possum - 05 Jul 2008 18:26 GMT
> > On Tue, 1 Jul 2008 09:24:15 -0700 (PDT),Lethal Possum
> > <lethal.pos...@gmail.com> wrote, quoted or indirectly quoted someone
[quoted text clipped - 12 lines]
> --
> Lew
Clearly the Foo/Bar classes relationship in my example is poorly
designed. Since Bar is not shipped with Foo, it should not use it
explicitly. It should instead use an interface implemented by Foo.
However I don't see any situation where the implementation of Bar
would make any difference to my Main class so I was surprised that the
presence of Foo in the classpath was strictly required.
Thank you for the input.
Cheers,
Thomas
Roedy Green - 06 Jul 2008 00:26 GMT
On Sat, 5 Jul 2008 10:26:45 -0700 (PDT), Lethal Possum
<lethal.possum@gmail.com> wrote, quoted or indirectly quoted someone
who said :
>Clearly the Foo/Bar classes relationship in my example is poorly
>designed. Since Bar is not shipped with Foo, it should not use it
>explicitly. It should instead use an interface implemented by Foo.
In general, when you want code to work with a class that does not yet
exist, or is not available, at the time of compilation, you define an
interface. The base code references only that interface. You tell
your clients that any new code must implement that interface. Then at
runtime you use Class.forName to load the new class, then use its
methods via the interface methods.
The Holiday calculator works this way. You can plug in you own code
for new Holidays. See http://mindprod.com/products1.html#HOLIDAYS
see http://mindprod.com/jgloss/classforname.html
Other places you see this provider/plugin approach are JDBC drivers
and JCE providers.

Signature
Roedy Green Canadian Mind Products
The Java Glossary
http://mindprod.com