In my Java application, I use a third-party library.
However, I found something strange, there are some nested packages, and some classes whose name may be the same as the name of the package.
I am afraid I can not make it clear. Here is an example:
package
com.xx.a
com.xx.a.a
And there is a class named 'a' inside the 'com.xx.a'.
So if I want to call this class 'a'...
I write:
a ma = new com.xx.a.a();
Then the IDE will think that I mean the package 'com.xx.a.a'.
Then I can not call it.
I wonder why?
By the way, it seems that the library provider did not want us to use these kinds of classes.
How do they do this?
The Java language allows class identifiers to be obscured by package identifiers. In your case the class com.xx.a is obscured by the package com.xx.a.
From the Java Language Specification:
6.3.2 Obscured Declarations
A simple name may occur in contexts where it may potentially be interpreted as the name of a variable, a type or a package. In these situations, the rules of §6.5 specify that a variable will be chosen in preference to a type, and that a type will be chosen in preference to a package. Thus, it may sometimes be impossible to refer to a visible type or package declaration via its simple name. We say that such a declaration is obscured.
I must say that the rules in §6.5 for classifying the meaning of an identifier are far from clear though.
The reason why you still happen to have a copy of a library that violates this rule is because the rule does not apply for class files / JAR files and the JVM.
This means that you can have such naming conflicts in JAR files, but you'll never see it as output from javac. The tool that has produced these class / package names is most likely a code obfuscator which produces this kind of messy code to compress the size of the files and to obfuscate the code to prevent reverse engineering.
PS. At a closer look it may actually be a bug on the Eclipse side (assuming that's the IDE you're talking about). By letting an empty package name collide with a class name, Eclipse chokes on something javac accepts. The spec is hard to follow, but from what I can see, javac follows the spec in this case.
This is a common issue when decompiling jars.
The Compiler will get confused when there is a class and a subpackage with the same name. If you don't find a compiler with the option to append a prefix regarding the type(package, class variable) you have to refactor the source files. You can do that with regex by for example renaming every package declaration and import from
import A.B.C
to something like
import pkgA.pkgB.C.
Of course you can't do that for the external packages from the sdk or other libraries but most of the time the used obfuscator renames them in the same way so for renaming to letters from A-Z you could use something like:
RegexFindAll("import\s+(?:[A-Z]\s*.\s*)*([A-Z])\s*.\s*(?:[A-Z]\s*.\s*)*[A-Z]\s*;")
RegexFindAll("package\s+(?:([A-Z])\s*.\s*)*([A-Z])\s*;")
And from there on you can rename every package. If your IDE doesn't offer such functionality you can also rely on the terminal with following commands.
To find all the files by name recursively(extendable with filename filter)
find -follow from https://stackoverflow.com/a/105249/4560817
To iterate over the found filenames
sudo find . -name *.mp3 |
while read filename
do
echo "$filename" # ... or any other command using $filename
done
from https://stackoverflow.com/a/9391044/4560817
To replace text inside a file with regex
sed -i 's/original/new/g' file.txt from https://askubuntu.com/a/20416
You need to do this:
com.xx.a.a ma = new com.xx.a.a();
Or import the package:
import com.xx.a;
a ma = new a();
The library is likely obfuscated (e.g. using proguard) to reduce size, prevent reverse engineering and "hide" stuff you're not supposed to use. Even if you manage to create an instance of this class, I would recommend against it, as you don't know what it will do or how it can/should be used.
we can not do this in java:
com.xx.A
com.xx.A.yy
the package name clashes with a class in the parent package,.
Related
As we know that the package statement is when triggered by the javac command, it creates the related directories according to the package name.
For example:
// MyClassB.java
package abc.xyz;
class MyClassA{
// ...
}
class MyClassB{
// ...
}
Now when I compile this file:
javac -d . MyClassB.java
After compilation, we will have the following directory structure having the .class files.
/
|___ abc
|___ xyz
|___ MyClassA.class
|___ MyClassB.class
Now the question is that, are these MyClassA.class and MyClassB.class contains only the MyClassA and MyClassB class's compiled bytecode?
What about the following statement?
package abc.xyz;
Is that statement removed during the compilation? Or this statement is only removed from the MyClassA class and remains in the MyClassB class because the name of the file was MyClassB.java. Or maybe the MyClassB.class file only contains the MyClassB class bytecode after compilation and the package abc.xyz; is removed during the compilation as well? If package abc.xyz; is not removed at the compilation time then it would be the problem when we will use this file at runtime because the runtime engine will also trigger the package abc.xyz; statement bytecode version, so, it would be a problem. Or it is removed during the compilation? Or the bytecode is only created for the classes but not for the file which contains the package abc.xyz; statement which results that package abc.xyz; is not become the part of the .class files.
The picture below clearly explains the question:
Can anyone explain what is the scenario behind this? It is my humble request to the contributors to not negative mark my question because i have some confusions regarding java, so, i want to clear my concept.If still my question is able for negative mark, then its Ok do, but at least answer as well.
Thanks !!!
Directives like the package declaration, as well as import statements, do not generate any code at all, they only affect how the compiler will treat names within the same compilation unit.
So a declaration like
package abc.xyz;
public class MyClassA {
}
creates a class with the qualified name abc.xyz.MyClassA or a class with the simple name MyClassA within the package abc.xyz; both means the same.
The qualified name is stored within the .class file, which is the only relevant information to the JVM.
There is the convention to store such file as SimpleName.class within a directory derived from the package name, i.e. abc/xyz/MyClassA.class, adhered by javac and used by the standard class loaders to look up the file when a class of that name is requested. Obviously, having such a convention makes life easier.
In principle, other storage methods are possible and there are some known. Starting with Java 9, the reference implementation has a module image format, before that, there was the “shared class data archive”, further, there was “Pack200”, an archive format that has been deprecated for removal with JDK 11.
Note that even jar files, which are extended zip files, are not exactly what you see. A zip file consist of a linear sequence of file entries having a qualified name, so in your case, you would have two entries reporting their names as abc/xyz/MyClassA.class and abc/xyz/MyClassB.class, whereas any directory structure shown by tools is actually derived from these names and does not implies that nested entries for abc and xyz actually exist. Still, the standard class loader will also use these entries the same way as if this directory structure existed, so there is no problem with the mindset of having stored the directory structure into the zip/jar file.
But starting with Java 9, there can be different versions for the same class, to be selected depending on the current Java version. The naming scheme used for that is not part of the convention used by javac when storing the files.
It does not go anywhere, it is compiled as class abc.xyz.MyClassA. You can see that if you would decompile that .class for example using javap.
If you open the .class file of any of your class than you can see the package details inside that. In your case if you will open MyClassA.class in text editor, then you can see something like abc/xyz/MyClassA. The the JVM will use it like abc.xyz.MyClassA. That's the reason if we need to use this class in some other class, we need to use it like import abc.xyz.MyClassA.
If one writes two public Java classes with the same case-insensitive name in different directories then both classes are not usable at runtime. (I tested this on Windows, Mac and Linux with several versions of the HotSpot JVM. I would not be surprised if there other JVMs where they are usable simultaneously.) For example, if I create a class named a and one named A like so:
// lowercase/src/testcase/a.java
package testcase;
public class a {
public static String myCase() {
return "lower";
}
}
// uppercase/src/testcase/A.java
package testcase;
public class A {
public static String myCase() {
return "upper";
}
}
Three eclipse projects containing the code above are available from my website.
If try I calling myCase on both classes like so:
System.out.println(A.myCase());
System.out.println(a.myCase());
The typechecker succeeds, but when I run the class file generate by the code directly above I get:
Exception in thread "main" java.lang.NoClassDefFoundError: testcase/A (wrong name: testcase/a)
In Java, names are in general case sensitive. Some file systems (e.g. Windows) are case insensitive, so I'm not surprised the above behavior happens, but it seems wrong. Unfortunately the Java specifications are oddly non-commital about which classes are visible. The Java Language Specification (JLS), Java SE 7 Edition (Section 6.6.1, page 166) says:
If a class or interface type is declared public, then it may be accessed by
any code, provided that the compilation unit (§7.3) in which it is declared is
observable.
In Section 7.3, the JLS defines observability of a compilation unit in extremely vague terms:
All the compilation units of the predefined package java and its subpackages lang
and io are always observable. For all other packages, the host system determines which compilation units are observable.
The Java Virtual Machine Specification is similarly vague (Section 5.3.1):
The following steps are used to load and thereby create the nonarray class or
interface C denoted by [binary name] N using the bootstrap class loader [...]
Otherwise, the Java virtual machine passes the argument N to an invocation of a
method on the bootstrap class loader to search for a purported representation of C
in a platform-dependent manner.
All of this leads to four questions in descending order of importance:
Are there any guarantees about which classes are loadable by the default class loader(s) in every JVM? In other words, can I implement a valid, but degenerate JVM, that won't load any classes except those in java.lang and java.io?
If there are any guarantees, does the behavior in the example above violate the guarantee (i.e. is the behavior a bug)?
Is there any way to make HotSpot load a and A simultaneously? Would writing a custom class loader work?
Are there any guarantees about which classes are loadable by the bootstrap class loader in every JVM?
The core bits and pieces of the language, plus supporting implementation classes. Not guaranteed to include any class that you write. (The normal JVM loads your classes in a separate classloader from the bootstrap one, and in fact the normal bootstrap loader loads its classes out of a JAR normally, as this makes for more efficient deployment than a big old directory structure full of classes.)
If there are any guarantees, does the behavior in the example above violate the guarantee (i.e. is the behavior a bug)?
Is there any way to make "standard" JVMs load a and A simultaneously? Would writing a custom class loader work?
Java loads classes by mapping the full name of the class into a filename that is then searched for on the classpath. Thus testcase.a goes to testcase/a.class and testcase.A goes to testcase/A.class. Some filesystems mix these things up, and may serve the other up when one is asked for. Others get it right (in particular, the variant of the ZIP format used in JAR files is fully case-sensitive and portable). There is nothing that Java can do about this (though an IDE could handle it for you by keeping the .class files away from the native FS, I don't know if any actually do and the JDK's javac most certainly isn't that smart).
However that's not the only point to note here: class files know internally what class they are talking about. The absence of the expected class from the file just means that the load fails, leading to the NoClassDefFoundError you received. What you got was a problem (a mis-deployment in at least some sense) that was detected and dealt with robustly. Theoretically, you could build a classloader that could handle such things by keeping searching, but why bother? Putting the class files inside a JAR will fix things far more robustly; those are handled correctly.
More generally, if you're running into this problem for real a lot, take to doing production builds on a Unix with a case-sensitive filesystem (a CI system like Jenkins is recommended) and find which developers are naming classes with just case differences and make them stop as it is very confusing!
Donal's fine explanation leaves little to add, but let me briefly muse on this phrase:
... Java classes with the same case-insensitive name ...
Names and Strings in general are never case-insensitive in themselves, it's only there interpretation that can be. And secondly, Java doesn't do such an interpretation.
So, a correct phrasing of what you had in mind would be:
... Java classes whose file representations in a case-insensitive file-system have identical names ...
I tried to add or remove a character from one of the class names and it worked. I feel it's always better to use different class names.
Don't think just about folders.
Use explicit different namespaces ("packages") for your classes, and maybe use folders to match your classes.
When I mention "packages", I don't mean "*.JAR" files, but, just the concept of:
package com.mycompany.mytool;
// "com.mycompany.mytool.MyClass"
public class MyClass
{
// ...
} // class MyClass
When you do not specify a package for your code, the java tools (compiler, I.D.E., whatever), assume to use the same global package for all. And, in case of several similar classes, they have a list of folders, where to look for.
Packages are like "virtual" folders in your code, and apply to all your packages on your classpath, or installation of Java. You can have several classes, with the same I.D., but, if they are in different package, and you specify which package to look for, you won't have any problem.
Just my 2 cents, for your cup of Java coffe
Is it possible to load a class by name if you don't know the whole package path? Something like:
getClassLoader().loadClass("Foo");
The class named "Foo" might be around, might not be - I don't know the package. I'd like to get a listing of matching classes and their packages (but not sure that's possible!),
Thanks
Nope. The Java ClassLoader.loadClass(String) method requires that class names must be fully qualified by their package and class name (aka "Binary name" in the Java Language Specification).
If you don't know the package, you don't know the name of a class (because it's part of the fully qualified class name) and therefore cannot find the class.
The Java class loading mechanism basically only allows you to do one thing: ask for a class with its fully qualified name, and the classloader will either return the class or nothing. That's it. There#s no way to ask for partial matches, or to list packages.
Contrary to the previous answers, and in addition to the answers in the question #reader_1000 linked to:
This is possible, by essentially duplicating the logic by which Java searches for classes to load, and looking at all the classfiles. Libraries are available that handle this part, I remember using Reflections. Matching classes by unqualified name isn't their major use case, but the library seems general enough and this should be doable if you poke around. Do note that this will, very likely, be a fairly slow operation.
Using java Reflections:
Class.forName(new Reflections("com.xyz", new SubTypesScanner(false)).getAllTypes().stream()
.filter(o -> o.endsWith(".Foo"))
.findFirst()
.orElse(null));
Even if you don't know the package name, sites like jarFinder might know it
i often have in java projects a lot of small helper("storage") classes like
2-Tuple, 3-Tuple, Point, .. (think you know what i mean)
Classes that mostly only have class variables, a constructor and getters/setters.
And in my current project, i wanted to store those small classes, that are often used in a lot of other classes in the project in a seperate package. But i do not really know how to name it (my motherlanguage is not english, but code should be for english readers.)
Hope you can give me an answer on this little questions.
Greetings
:)
Different people would name these differently as the names are a matter of personal choice.
A few options:
If the storage classes conform to the Javabeans conventions, you could add the suffix "Bean" eg PointBean
I have also seen a suffix of "DO" or "VO" being used to denote a "data object" or "value object". eg PointDO
You could leave the class name as is eg Point. However if you feel that it does not convey the fact it is a storage class, try to make the package name convey that fact eg
com.xyz.valueobjects.Point
or
com.xyz.dataobjects.Point
or
com.xyz.storage.Point
Personally I like to use style #3.
I'd stick those kinds of classes in a *.util package.
I'd go with something like:
utils or the more verbose utilities
you can then break that down further if you need to:
utils.data for data-related utility classes, for example.
Additionally, there's a question here on whether to pluralise or not: Naming convention for utility classes in Java
For your information the "storage classes" you are referring to are called Java Beans. Using the popular model view controller design pattern; these would be your model. So lets say you want to put them in a package called model and the domain name of your company is mycompany.com, then the propper java naming convention would be com.mycompany.model; add this line as the first line of code (before any import statements) to all of your Java bean classes:
package com.mycompany.model;
You must also move your Java bean files into a folder structure that is the same. Lets say the file with your main method is in the directory /%ProjectHome%/, then your Java Beans go in a folder /%ProjectHome%/com/mycompany/model/
To compile these files you will now have to change to your /%ProjectHome%/ directory then type javac com/mycompany/model/*.java
Then you will be able to import these files from your other java classes by typing
import com.mycompany.model.*;
Also note, that the Java convention for package names is all lower case, as not to clash with the name space of Class names.
Hope this helps.
I believe what you're doing is using objects purely for storing data (no behaviour). And since you're talking about tuples, I assume these are used for transferring data to/from your database, so perhaps just "data objects"?
Whenever I use nested classes, I give them names not including the outer class name, e.g.,
MySomething.Kind and not MySomething.MySomethingKind. The nested classes are sometimes visible to the outside and then I want to refer to them always by the name qualified by the enclosing class, i.e., MySomething.Kind and not just Kind. Sometimes there are multiple classes containing a nested Kind, so using the unqualified name may be confusing.
Is there any way to prevent Eclipse from needlessly importing mypackage.MySomething.Kind instead of using (already imported) mypackage.MySomething together with the semi-qualified name?
UPDATE:
This doesn't happen spontaneously. As stated by jprete, when I always use the semi-qualified name, the nested class doesn't get imported. But any refactoring creating a variable of type MySomething.Kind declares it as Kind only and adds the unwanted import statement. This turns the refactoring to useless, as I have to edit it manually. Whenever I forget, I get the worst of both: a mixture of unqualified and semi-qualified names.
I have found that, if I always refer to the nested class with the "semi-qualified" name - i.e. MySomething.Kind rather than Kind - that Eclipse will not try to automatically add import mypackage.MySomething.Kind when I tell it to reorganize imports, but instead will only add import mypackage.MySomething and leave the "Class.NestedClass" references alone.
It looks like there's no solution, but what I'm doing now is pretty practical (when used in a script):
find src -name "*.java" | xargs perl -pi -e \
's/^(import [.\w]+\.)([A-Z]\w+)(\..*);/$1$2;/;'
It simply replaces all the unwanted imports like
import java.util.Map.Entry;
by the outer class imports like
import java.util.Map;
It's matter of seconds to fix the errors manually and let organize imports get rid of duplicates. It ignores static imports as I want.
Warnings:
it touches all files (solution)
you have to refresh them in eclipse
it modifies the source code, which is something nobody should do without a source control