The following snippet (abstracted from real-world code) compiles and runs in Eclipse.
package1/Outer.java:
package package1;
import package1.Outer.Mid.Inner;
import package2.Bar;
public class Outer {
final Mid mid = new Mid();
public Outer() {
mid.setInner(new Inner() {
#Override public void foo() {
System.out.println("In Outer.foo()");
}
});
}
public static class Mid implements Bar {
private Inner inner;
public void setInner(Inner inner) {
this.inner = inner;
}
public Inner getInner() {
return this.inner;
}
#Override
public void bar() {}
interface Inner {
void foo();
}
}
}
package2/Bar.java:
package package2;
public interface Bar {
void bar();
}
However, it fails with this error when compiling using javac:
package1\Outer.java:31: cannot find symbol
symbol : class Bar
location: class package1.Outer
public static class Mid implements Bar {
^
package1\Outer.java:42: method does not override or implement a method from a supertype
#Override
^
2 errors
Now, if I switch the order of the import statements, like so:
import package2.Bar;
import package1.Outer.Mid.Inner;
...then it compiles in both Eclipse and javac. Clearly the order of the import statements seems to matter...but why?
Notes:
I tested this using Java JDK 1.6.0_30, and also Java JDK 1.7.0_21. If this is a bug that has since been fixed, that would be good to know.
It seems strange to me that the package1.Outer.Mid.Inner import is even necessary, given the Inner interface is nested within Outer.java, but both Eclipse and javac seem to require it
I discovered this problem trying to run an Ant build of production code that had a similar structure. Everything was building fine in Eclipse, but the Ant script just refused to go through.
This looks like a bug, as reported on Oracle's bug database here.
According to the JLS §7.5, the order of import-statements should not matter.
Related
I'm trying to figure out how to resolve the following use case:
A user can define a Java class, say MyFoo like this (the code is generated automatically and the guid is set to avoid name collisions)
import com.bar.entities.BaseEntity;
package com.bar.entities.defined.79B35635_DEDF_A344_9621_0477A6E71725;
public class MyFoo extends BaseEntity {
public String getFoo() { ... }
public Integer getBar() { ... }
#Override
public byte[] serialize() {...}
}
A user can import that Java class in other class like this:
package com.bar.business;
import com.bar.entities.defined.79B35635_DEDF_A344_9621_0477A6E71725.MyFoo;
public class Business extends Minion {
#Override
public void execute(EntityProvider ep) {
MyFoo myFoo = ep.get("myFoo", MyFoo.class);
if (myFoo.getBar() < 10) {
notifyMaster();
}
}
}
Additionaly I have a Java application (actually a Grizzly HTTP Server with a Jersey API Rest) that must be able to compile both classes.
¿How can I compile both classes? The first one can be compiled. The second one can't because the compiler says that the method getBar() is not known (it does not fail at the import line, surpringsly)
I want to send both Class<?> instances over the network and use them elsewhere via reflection with getDeclaredConstructor()
Kind regards.
My teammate may have found an interesting bug in jdk compiler.
It seems to depend on import sequence when trying to compile a class with 3-level nested class.
FrameLayoutCompat.java
package com.external;
public class FrameLayoutCompat
{
}
First.java
package com.nested;
import com.nested.First.Second.Third;
import com.external.FrameLayoutCompat;
public class First
{
public static class Second extends FrameLayoutCompat
{
public static class Third
{
}
}
public static class Second2
{
public static class Third2
{
private Third mThird;
}
}
}
Try to compile FrameLayoutCompat.java First.java
javac com/external/FrameLayoutCompat.java
javac com/external/First.java
Here's the error.
com/nested/First.java:8: error: cannot find symbol
public static class Second extends FrameLayoutCompat
^
symbol: class FrameLayoutCompat
location: class First
1 error
But if we change the code by switching the import sequence. It works.
package com.nested;
import com.external.FrameLayoutCompat;
import com.nested.First.Second.Third;
public class First
{
public static class Second extends FrameLayoutCompat
{
public static class Third
{
}
}
public static class Second2
{
public static class Third2
{
private Third mThird;
}
}
}
We tried the ecj, eclipse java compiler, it works also, so it may be a bug in Oracle jdk compiler.It works. Any comment?
java -jar ../ecj-4.4.jar -source 1.8 .
[Edited]
I have try this to compile. Because other thinks it's better to compile them together. But the problem is that: the import sequence matters, instead of the 'cannot find symbol' error. Because if you try to change the sequence of import, it works well.
javac -s ../src -d ../build com\external\FrameLayoutCompat.java com\nested\First.java
Here's the zip file I saved in Evernote. It's worth a try. https://www.evernote.com/shard/s70/sh/8ed81644-5b90-4008-aa33-3b9e3aa7904a/5f8ba97f96d2132b
[edit]
Find a JPE for this, it's an know issue in JDK for javac command.It can be closed.
JEP 216: Process Import Statements Correctly
The reason is "extends FrameLayoutCompat":
When you use,
import com.nested.First.Second.Third;
import com.external.FrameLayoutCompat;
you are trying to import the class called "Third" which exists within class Second which exists within class First.
But the Second class is declared as:
public static class Second extends FrameLayoutCompat.
So it needs FrameLayoutCompat without which it can't compile. And if class Second doesn't compile, then class First won't compile too. And hence the error:
Try commenting
// extends FrameLayoutCompat
and instead use
FrameLayoutCompat layout;
and you'd find the order doesn't matter
Hope this helps.
So I thought I understood packages, but turns out I don't.
Classes inside a package: I have this folder structure: mypackage/mysubpackage. Inside mysubpackage folder I have 2 classes:
package mypackage.mysubpackage;
public class Class1 {...}
and
package mypackage.mysubpackage;
public class Class2 {...}
However, when I compile Class1 (which uses methods from Class2) using javac Class1.java inside the directory mypackage/mysubpackage, it can't see Class2:
Class1.java: error: cannot find symbol
Class2 c = new Class2();
^
symbol: class Class2
location: class Class1
It works fine if I run javac Class1.java in the directory that contains mypackage/mysubpackage. Shouldn't the compilation work inside mysubpackage folder?
Classes in another package: Now, I have another class with methods that I want to be accesible to all the subpackages, so I create a final Commons.java inside mypackage/commons:
package mypackage.commons;
public final class Commons {
public static double method() {...}
...
}
And then I update Class2 importing that class so that I can use its methods inside the class:
package mypackage.mysubpackage;
import mypackage.commons.*;
public class Class2 {...}
Now it doesn't find the method I defined in the final class:
./mypackage/mysubpackage/Class2.java: error: cannot find symbol
double var = method();
^
symbol: method method()
location: class Class2
Shouldn't if find it? I think I'm importing it correctly, the methods are static and the class is final. Why doesn't it recognize it?
Cheers!
Looks like your problem is with where you set your working directory when you launch the Java compiler from the command line.
I would recommend that you pick up an integrated development environment -- Eclipse is a good one. Using the IDE you run into no such problems. Here are the classes I just created in Eclipse, which compile correclty.
Commons
package com.example.packagecommons;
public class Commons {
public static double method() {
return 0;}
}
Class1
package com.example.packages;
public class Class1 {
private Class2 c2;
public Class1() {
c2 = new Class2();
}
}
Class2
package com.example.packages;
import com.example.packagecommons.Commons;
public class Class2 {
private double initialValue;
public Class2() {
initialValue = Commons.method();
}
public double getValue() {
return initialValue;
}
}
Suppose your two classes Demo01 and Demo03 are in package pack1.subpack and your Demo02 is in pack2
So the hierarchy is like
someDrive/pack1/subpack/Demo01
someDrive/pack1/subpack/Demo03
someDrive/pack2/Demo02
someDrive/pack1/common/Demo04
where Demo01 is
package pack1.subpack;
import pack2.Demo02; // need to add this if calling class of different package
import pack1.common.Demo04; // if you are going to use Demo04 class in Demo01 class
public class Demo01 {
public void run() {
System.out.println("--running Demo01-");
}
public static void main(String[] args){
Demo01 demo01 = new Demo01();
demo01.run();
Demo02 demo02 = new Demo02();
demo02.run();
Demo03 demo03 = new Demo03();
demo03.run();
Demo04.run();
}
}
Demo02 is
package pack2;
public class Demo02 {
public void run() {
System.out.println("--running Demo02--");
}
}
Demo03 is
package pack1.subpack;
public class Demo03 {
public void run() {
System.out.println("--running Demo03--");
}
}
Demo04 is
package pack1.common;
public final class Demo04 {
public void run() {
System.out.println("--running Demo04--");
}
}
Then just compile it using javac pack1/subpack/Demo01.java
and execute it using java pack1/subpack.Demo01
I know this thread is old but I'd like to clarify things so as to help future viewers.
Your first question basically is, how does the Java run-time system know where to look for packages that you create? Remember these 3 rules (one of them must apply):
Your main package must be in a subdirectory of the current working directory to be found.
You can specify a directory path or paths by setting the CLASSPATH environmental variable.
You can use the -classpath option with java and javac to specify the path to your classes when you are executing your code via the terminal/cmd.
To answer your first question, you are executing your code from mypackage/mysubpackage. For Java run-time to recognise Class2, you must execute from mypackage.
Coming to the second question, when you import all the contents of a package using *, you need to refer to static class members by explicitly writing the class name before them, as Java does not know which class in the package you are referring to. Hence, in your code, you must write
Commons.method() instead of just method(). If you do not want to prefix the name of the class time and again, you can explicitly import the specific class you want. In your case, this would be mypackage.commons.Commons. Then you can call method() directly (provided it is static).
sh$ cd package/subpackage
sh$ javac Class1.java
Will lead to an error as the compiler will try to locate Class2 in the package/subpackage subdirectory of the current directory.
You have to compile that way:
sh$ javac package/subpackage/Class1.java
Here is a complete working example:
sh$ cat pkg/subpackage/Class1.java
package pkg.subpackage;
import pkg.commons.Class2;
public class Class1 {
public static void main(String args[]) {
Class2.doSomething();
}
}
sh$ cat pkg/commons/Class2.java
package pkg.commons;
public class Class2 {
public static void doSomething() {
System.out.println("hello");
}
}
sh$ javac pkg/subpackage/Class1.java
sh$ java pkg.subpackage.Class1
hello
Say I am using a closed source java library with a known buggy class, say BuggyClass and this class is hardcoded throughout the rest of the library. So I would imagine that the java library looks something like this:
public class BuggyClass {
public T buggyMethod (...) {
// Buggy code here
}
}
with several other classes in the library that make use of this class:
public class Example {
private BuggyClass = new BuggyClass(); // No dependency injection possible
public Example (/* No way to pass in my own subclass of BuggyClass*/) {
// ...
}
}
etc...
Is there any hack, or workaround, possibly using the class loader so that I could subclass BuggyClass and get Example (and every other class in the library that has BuggyClass hardcoded in) to use my subclass?
You can't do a subclass, no, but you can write your own BuggyClass entirely and make sure it appears earlier in the classpath than the real one. I don't think it's documented, but the default classloader seems to typically use the first matching class it finds.
But obviously this is a Really Bad Option, so you want to exhaust every other avenue before trying to solve temporarily work around the underlying problem this way.
Example: Suppose we have this:
// The "buggy" class
package somepackage;
public class BuggyClass {
public String someMethod() {
return "I'm in the buggy class";
}
}
and this:
// Uses the "buggy" class
package somepackage;
public class BuggyClassUser {
public String useBuggyClass() {
BuggyClass c = new BuggyClass();
return c.someMethod();
}
}
compiled and the classes in buggy.jar. Then we have this test class:
import somepackage.*;
public class Test {
public static final void main(String[] args) {
BuggyClassUser u = new BuggyClassUser();
System.out.println(u.useBuggyClass());
}
}
If we run that (*nix format classpath):
java -cp .:buggy.jar Test
...we see
I'm in the buggy class
But if we create a somepackage directory and put this in it:
package somepackage;
public class BuggyClass {
public String someMethod() {
return "I'm in the fixed class"; // <== Difference here
}
}
...and compile that, since we have that in our classpath in front of the jar, this command:
java -cp .:buggy.jar Test
...now gives us this:
I'm in the fixed class
Again, this is very, very much a workaround, not a solution.
The Code:
package com.keyoti.rapidSpell;
import java.util.Comparator;
// Referenced classes of package com.keyoti.rapidSpell:
// RapidSpellChecker
class RapidSpellChecker$CompareL
implements Comparator
{
public int compare(Object a, Object b)
{
return (int)(100D * (suggestionScore2b(topWord, (String)b) - suggestionScore2b(topWord, (String)a)));
}
public void with(String w)
{
topWord = w;
}
private String topWord;
RapidSpellChecker$CompareL()
{
}
}
This is the one the many classes in the application.
What does the $ sign in class RapidSpellChecker$CompareL implements Comparator signify?Is it simply the class name or has some significance?
I suspect this is decompiled code. (See at the bottom for more information.) The $ shows that it's a nested class within RapidSpellChecker. So the code would originally have looked something like this:
public class RapidSpellChecker
{
// Other code withing RapidSpellChecker
static class CompareL implements Comparator
{
// Code for compare, with etc
}
}
I've shown this as a static nested class, because the code you've shown doesn't have any implicit reference to an instance of RapidSpellChecker. If it did, the original code would have been like this:
public class RapidSpellChecker
{
// Other code withing RapidSpellChecker
class CompareL implements Comparator
{
// Code for compare, with etc
}
}
In this case it's an inner class.
See the Java tutorial on nested classes for more information.
EDIT: I originally thought this was invalid code; that you couldn't use $ in an identifier in Java to start with. It turns out I'm wrong. From the Java Language Specification, section 3.8:
The $ character should be used only in mechanically generated source code or, rarely, to access preexisting names on legacy systems.
So it's valid, just discouraged.
That's a nested class. When the Java compiler compiles a class with nested classes, it separates all of them in different .class files.
class A {
class B {
}
}
gives A.class and A$B.class
You can use $ in a variable name if you want. In a variable name it has no special significance.
$ is also typically used to indicate inner classes when you compile using javac
If you compile
class A {
class B {
}
}
You'll see A.class created and B.class.
For fun and amusement, you could create confusing looking "JQuery"-esque code in Java (you need the static import to use the $ static method). See the example below:
import static thisPackage.*;
public class $ {
public static $ $(String s) { return new $(s); }
public $ fadeIn(int fade) { return this; }
public $ slideUp(int slide) { return this; }
public $ delay(int ms) { return this; }
public $(String s) { }
public static void main(String[] args) {
$("#foo").slideUp(300).delay(800).fadeIn(400);
}
}
Implementing this with a DOM library underneath would be a fun project!