I suspect the answer is no, but I want to check. If I have this structure in a Java application:
-package
-ClassA
-subpackage
-ClassB
-subsubpackage
-ClassC
I want package.subpackage.ClassB to have access to package.subpackage.subsubpackage.ClassC, but I don't want package.ClassA to have access to package.subpackage.subsubpackage.ClassC. Is there any way to enforce this?
No, the only access modifiers are:
public - global access
protected - package and subclass access
"package-private" (no modifier) - package access
private - class access only
protected and package-private doesn't recursively grant access to subpackages. In short, subpackages don't really have any relationship with their parent package except for the fact that they share the same name prefix.
Here is a Java Specification Request that (I believe) deals with this issue: http://jcp.org/en/jsr/detail?id=294
This was supposed to be implemented in the just recently released Java 7, but apparently has been forgotten.
The only way that you can get this to work is through the use of inner classes. And, honestly, if that doesn't work then maybe you should be asking, "What is so important about C that A shouldn't be able to instantiate it but B and at least one other class can?"
No, there isn't. A package name is not an hierarchical construct If we have a class
foo.bar.Application
then bar is not a child-package of foo. The package is foo.bar and there is no relation between foo and foo.bar, they are totally different packages (namespaces).
Making ClassC as an internal class for ClassB may solve your task.
No. The package hierarchy in Java has no effect on package visibility.
Things are either totally public or package-private.
There is no such thing as a "friend" package or a "parent" package here.
Related
I have a question about a sneaky way to gain access to package-access members that occurred to me. Specifically, I want to extend a class - let's call it com.acme.Foo - to add some functionality. This is pure addition: all current methods of Foo would be supported just by delegating to the superclass's method. However, there is no accessible constructor of Foo, so I can't extend the class and my implementation won't pass the "isA" test for being a Foo. There is no interface that expresses Foo-ness that I can use instead of inheritance.
This is where the sneaky thought occurred to me: Foo has a package-access constructor, so why not just create a package com.acme in my source folder and create an InheritableFoo class in that package:
package com.acme;
public class InheritableFoo extends Foo {
public InheritableFoo() {
super();
}
}
Now I can implement my extension to Foo by extending InheritableFoo, since that DOES have an accessible constructor.
This feels all wrong, and somehow unethical: I'm not respecting the decision the original programmer made when he or she decided to not expose a constructor for Foo. But it could be the case that they simply adopted the "deny everything until there's a reason for it" approach. Either way, is there anything fundamentally wrong with this idea? Am I going to cause myself problems in the future if I go this route, and if so, why?
You are correct regarding the fact that you can "sneak" in a class of your own into another package and get access to package restricted elements.
This will however only as long as the author of com.acme.foo distribute his code as source or an unsealed package. Should the author of this package distribute it as a sealed jar (which is quite common I believe) your approach will no longer work.
From Sealing Packages within a JAR File
Packages within JAR files can be optionally sealed, which means that
all classes defined in that package must be archived in the same JAR
file.
Only for example lets consider the class ClassFileAssembler from the sun.reflect package.
This class is a package local class:
class ClassFileAssembler implements sun.reflect.ClassFileConstants {...
So we can not use even its name ClassFileAssembler, we can not import it directly - it will lead to a compiler error.
However we can create a package named sun.reflect in our project and use the ClassFileAssembler name internally in this package - Java compiler will think that we are inside the ClassFileAssembler's package.
If so, why not to try to get a reference to a class object, i.e. ClassFileAssembler.class?
Class<ClassFileAssembler> classFileAssemblerClass = ClassFileAssembler.class;
Unexpectedly this code leads to a run-time error: java.lang.IllegalAccessError: tried to access class sun.reflect.ClassFileAssembler from class sun.reflect.Test.
However we still able to get the ClassFileAssembler class object:
Class<ClassFileAssembler> aClass = (Class<ClassFileAssembler>)Class.forName("sun.reflect.ClassFileAssembler");
It works fine and gives us a full class description.
So, the questions are:
1) What is the difference between techniques, how Class.forName0 retrieves reference to class object, and how .class does it?
2) Why do they have such different security checks?
3) What's the reason to protect .class reference in such way?
4) Do these techniques use different class loaders?
Class.forName don't care about whether a class is package local or not. It is when you attempt to use that class that access is checked. BTW if you do setAccessible(true) you can by pass these access restrictions.
The Reflection library allows you to do many things you cannot do in Java code. The Java has rules as to what you can and cannot do. e.g. you cannot set a final field outside a constructor or more than once. Note: the JVM doesn't have this restriction and at runtime you can use reflections to change it.
The reason this class is package local is to restrict access of the class to code outside this package. This doesn't mean you cannot access it if you really try, but it is less likely you will access it without serious thought being put into it. e.g. when I import classes in my IDE it often suggests classes from com.sun.* which are unlikely to be the right choice. (MY IDE can be set up to ignore these, but I often seem for find some new package I don't want)
The reason Reflections can do this is to support functionality such a Serialization. With Serialization you need to be able to serialize class outside the package of the Serialization library and obtain fields and reset them when deserializing. Reflections is also used by many Inversion of Control libraries though I suspect this is not what they had in mind when they design it.
If you check the javadoc of Class#forName, you will see that:
Note that this method does not check whether the requested class is accessible to its caller.
there is no difference. but you cannot access the static field .class of the package private (no modifier) class ClassFileAssembler.
everyone could access the Class instances, but the fields are protected.
in fact no one designed to protect .class reference this way, it's side effect of protecting other fields.
i dont think so.
I'm having a strange problem that I can't figure out that popped up when trying to pluginize my program. An additional problem is that I'm not able to create a simple test case, because every time I try it works. There must be some complication I'm missing. But I'll try to describe the situation as clearly as possible, in case it sounds familiar to anyone.
I have a base class called Seed which is part of the main application and loaded by the system classloader. I have a plugin which contains a class Road which is a subclass of Seed. It is loaded at runtime from a separate jar file. The class Road references the field Seed.garden, which is defined as:
protected final Garden garden;
Note that I don't get compilation errors. I also don't get runtime errors when the plugin jar is included on the system classpath. Only when my main application loads the plugin using a new classloader (that has the system classloader as its parent) do I get the error. The error is:
java.lang.IllegalAccessError: tried to access field package.Seed.garden from class package.Road$4
It must have something to do with the fact that the subclass has been loaded by a different class loader than the superclass, but I can't find any official reason why that shouldn't work. Also, like I said, when I try to reproduce the problem with a simple test case (that includes the separate jars, loading the subclass with a different classloader, etc.), I don't get the error.
It also doesn't seem likely that I'm violating the access rules since it works when the classes are loaded by the same classloader, and I don't get compilation errors.
I'm out of ideas! Does anyone recognise this problem, or have some pointers for me for directions in which to look? Help!
OK, so with the help of axtavt and other respondents I figured out what the problem is. The other answers helped, but they didn't get it exactly right, which is why I'm answering my own question. The problem turned out to be the concept of "runtime packages", defined in the Java Virtual Machine specification as follows:
5.3 Creation and Loading
...
At run time, a class or interface is determined not by its name alone, but by a pair: its fully qualified name and its defining class loader. Each such class or interface belongs to a single runtime package. The runtime package of a class or interface is determined by the package name and defining class loader of the class or interface.
...
5.4.4 Access Control
...
A field or method R is accessible to a class or interface D if and only if any of the following conditions is true: ...
R is protected and is declared in a class C, and D is either a subclass of C or C itself.
R is either protected or package private (that is, neither public nor protected nor private), and is declared by a class in the same runtime package as D.
The first clause explains why Road is allowed to access Seed.garden, since Road is a subclass of Seed, and the second clause explains why Road$4 is not allowed to access it, despite being in the same package as Road, since it is not in the same runtime package, having been loaded by a different class loader. The restriction is not actually a Java language restriction, it is a Java VM restriction.
So the conclusion for my situation is that the exception occurs due to a legitimate restriction of the Java VM, and I'm going to have to work around it, probably by making the fields public, which is not a problem in this case since they are final, and not secret, or perhaps by exporting Seed.garden to Road$4 via Road, which does have access.
Thank you everyone for your suggestions and answers!
Sounds like you have a class identity crisis, having two different class loaders loading the same class in the class hierarchy or similar. Read up some on the java class loaders. Here is a good one for introduction, for "class identity crisis" see figure 2: http://www.ibm.com/developerworks/java/library/j-dyn0429/
I should add that Road$4 is an anonymous inner class of Road...
Someone else thought this was a bug as well back in 1998:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4116802
An inner class has no greater access to members of another class than
a top-level class, except for those members that are declared within
an enclosing, enclosed, or sibling class. It is a common misconception
that an inner class has unrestricted access to inherited members of
its enclosing classes. This is not true.
I would probably research that fact a bit more though because this was reported originally against Java 1.2, but I seem to remember from my reading that this is true today as well.
EDIT:
I confirmed this to be true:
http://docs.oracle.com/javase/tutorial/java/javaOO/summarynested.html
The scope for an anonymous inner class is only the point where it is defined. So it will not have access to inherited members, even if the outer class does.
This is permission error, so it depends on the framework you use to run your runtime.
Just to clarify this is indeed this, make the parent member public, and then try to run. In case everything is ok, then restore your code, and according to the runtime you use we need to configure the correct security access.
I plan to extend a JSF renderer. The package name is oracle.adfinternal.view.faces.renderkit.rich
Should the extended class be in the same package structure:
oracle.adfinternal.view.faces.renderkit.rich
or even oracle.adfinternal.view.faces.renderkit.rich.[subpackage]
or can/should I put it into my own package? com.company.renderkits.
I suppose package-private variables might be interfered with if I put this into my own package name?
Any thoughts?
In general, you should put your extended classes in your own package.
The package-private access level should be used by closely connected classes, possibly written by the same developer, who knows all the implementation details. This is not the case if you extend a class with some functionality.
All that said, there are a few occasions where you need to place your class in the package of the superclass as a workaround, but remember, this is an ugly hack, and try hard to avoid it.
You should put the class into your own package. There shouldn't be any compilation/access problems. The superclass already sees what it needs to see.
I suppose package-private variables might be interfered with if I put this into my own package name?
This is true, but normally the extending class shouldn't worry about this. The to-be-extended class would have used the protected modifier for this otherwise.
You should not add things to other entities (companies, person) packages. They could make a class with the same name (and same package of course) at a later date. They could also choose to seal their JAR files as well which would prevent you from adding classes to their packages.
The purpose of packages is to give each entity their own unique namepsace in which to create types.
In your case I would name the package something like: com.foobar.oracle.adfinternal.view.faces.renderkit.rich where "com.foobar" is the reverse domain name of your entity (if you don't have one pick something that is unique).
I realise that this is a very basic question, but it is one which has always bothered me. As I understand things, if you declare a field private in Java then it is not visible outside of that class. If it is protected then it is available to inherited classes and anything in the same package (correct me if either of those definitions is incorrect).
Does this mean it is not possible to declare a field that is accessible to only inherited classes and not other non-inherited classes in the same package?
I appreciate that there are ways around this, but are there instances when you would want to have this sort of behaviour?
Obviously the above question applies to methods as well as fields.
Many thanks.
See: http://java.sun.com/docs/books/tutorial/java/javaOO/accesscontrol.html
Package > Subclasses, you can never have a field only visible by subclasses but not by classes from the same package.
Basically:
private: Accessible only by the class.
public: Accessible by any class.
protected: Accessible by the class, all inherited classes and the classes of the current package (edited).
no scope defined: Accessible by all classes of the current package.
more information here.
Yes, Java's protected access is a little bit odd in that way. I can't immediately see why it's desirable at all. Personally it doesn't bother me for fields as I don't like non-private fields anyway (other than constants) but the same is true for other members.
.NET doesn't have the concept of package/namespace access visibility at all, but it has an alternative which is assembly (think "jar file" - not exactly the same, but close). Frankly I'd like to have namespace and deployment-unit visibility options, but it seems I'm doomed to disappointment...