Community
Participate
Working Groups
When I have a class called 'foo.Bar' and perhaps a resource file called 'foo/Bar/messages.properties', Eclipse fails to compile those files by reporting the following errors: - Bar cannot be resolved to a type - The type Bar collides with a package It looks like the compiler is confused by having the class and the package in the same name, yet there's no such prohibition in JLS. Sun's javac accepts the same source code happily. I'll attach the test case.
Created attachment 53463 [details] Small java project to reproduce this problem Import this into Eclipse to see the problem. Also run the build.xml in it to see that javac takes it fine.
But what happens when you define your first class in the package foo.Bar ? javac complains about a collision for these types: package foo; public class Bar {} package foo.Bar; public class X {} -> foo/Bar/X.java:1: package foo.Bar clashes with class of same name package foo.Bar; Since JDT is an incremental development environment, we report type/package collisions once they occur.
Ah, but I'm not defining classes in "foo.Bar". They are really just for housing resource files. If having a class in the "foo.Bar" package is a necessary condition for a collision, then I think JDT is still wrong in reporting an error before that condition is met.
But JDT is an incremental development environment, so creating a package means you want the package to appear in the namespace used to resolve types/packages, and before the first type is created. Why do you need to place the resource files in this specific package?
I'm not sure why being an incremental development environment would have anything to do with this. Assuming that the javac behavior is correct, it looks like the expected behavior is that a package must not "appear in the namespace used to resolve types/packages" until the first type is created in it. If what you are saying is that this is a Sun's javac bug and not JDT's bug, that would make sense. But I don't quite understand your reasoning so far. As for the reason why we want to place the resource files in this specific package, it was simply just because that's how it's done, and I can't change it without breaking a backward compatibility. This issue has been brought to my attention relatively recently by one of a new developer to my project who uses Eclipse. Before that, no one was using Eclipse, so we didn't notice this problem.
Kohsuke - what does your command line looks like when compiling ? >javac Bar.java Bar\X.java Bar.java:2: foo.Bar clashes with package of same name public class Bar {} ^ Bar\X.java:1: package foo.Bar clashes with class of same name package foo.Bar; ^ 2 errors If you only compile Bar.java, it would work fine, but if you try to compile both at once, you will see the problem you get in Eclipse as well (since by default it will compile both files). You may configure exclusion rules to selectively exclude some files from your source folder.
The JLS does not specify when a package must appear, so there is no right or wrong in this case. But because JDT is an incremental development environment, we do not postpone reporting errors until another type is added to an empty package. We expect you to add types to packages that you've created, so we treat the new package as a package from the start. Here is another example if you define an empty package foo.Bar : package test; // Can I refer to foo.Bar.Member? What happens when foo/Bar/X.java is created? import foo.Bar.*; public class Test {...} package foo; public class Bar { public class Member {} }
Philippe - thank you for chiming in, but please take a look at the attached workspace. I do not define any class in the Bar package. There's only a resource file. If I were defining classes, that would have been an error in both JDT and Sun javac. I opened this issue only because my project that compiles with javac (and IntelliJ) doesn't work with JDT. Kent - I'm not really an JLS expert, but it seems like a package is considered a package only when there's a source file in it. IOW, JLS operates on a set of source files and not a directory tree, and therefore empty directories (or directories that do not have source files) do not affect the compiler behavior. Because of that, I don't agree with "there is no right or wrong in this case." I'll check with javac guys as they are much more familiar with JLS than I am. As with your example, javac is quite consistent. If there's no "foo/Bar/*.java", then "import foo.Bar.*" refers to the inner Member class. If there's any "foo/Bar/*.java", then it's a compilation error, presumably for the exact reason you cited in the comment. I still don't think just because JDT is an incremental environment does not make it impossible to handle this correctly. I think the bug that I'm pointing out is that just because I created a source folder does not immediately mean there's a new Java package. There needs to be one bit flag on each directory in the source tree that says whether that is a "package" in the JLS sense (and AFAICT, Eclipse already seems to have such information, as the package icon renders differently.) In various other places, JDT already needs to deal with a situation where a change in one part of the source tree affects how another part of the source tree is interpreted. Isn't this just one more of those? If I were working on a fresh project, I wouldn't try this potentially risky convention, but I'm really hoping to have some of my developers use Eclipse, and this bug is preventing us from doing that.
Just so we're clear: javac != JLS. So "a package is considered a package only when there's a source file in it" is a consequence of javac's current implementation and NOT a requirement specified by the JLS.
From JLS 7.2: Each host determines how packages, compilation units, and subpackages are created and stored, and which compilation units are observable (ยง7.3) in a particular compilation. The observability of compilation units in turn determines which packages are observable, and which packages are in scope. I think this is saying that JDT as a "host" can stochoose to decide how to manage packages, but it says the observability of the package is determined by the observability of compilation units. This is further reinforced by the following section: From JLS 7.4.3: A package is observable if and only if either: * A compilation unit containing a declaration of the package is observable. * A subpackage of the package is observable. So I think it's clear enough that a package which does not contain any compilation unit is not observable.
Released for 3.3 M4 in HEAD stream. Changed testcase in FromPRsTest.test1FYYKRJ() to expect a warning instead of an error when a type collides with a package. There's nothing like changing the spec to match an implementation.
Thank you for a quick turn around! Much appreciated.
Verified for 3.3M4 with I20061212-0010.