Java experts, I would sincerely appreciate any insights!
I have an abstract class in a package with a protected method. I also have a subclass of this class in the same package. Now, when I try to instantiate the subclass from a class outside the package, and invoke the protected method on the subclass' instance, Eclipse is complaining the protected method is not visible.
I thought, protected methods will be visible to all children - in or out of the package - as long as the class visibility does not restrict it - in this case, both the parent and the child class are public. What am I missing? Thanks in advance!
package X;
public abstract class Transformation {
protected OutputSet genOutputSet (List list) {
..
}
}
package X;
public class LookupTransformation extends Transformation {
}
package Y;
import X.*;
public class Test {
public static void main(String[] args) {
List<field> fld_list = new ArrayList();
..
LookupTransformation lkpCDC = new LookupTransformation();
OutputSet o = lkpCDC.genOutputSet(fld_list); // Eclipse errors out here saying genOutputSet from the Type Transformation is not visible. WWWWWWWWHHHHHAAAATTTTTT????
}
}
protected access means genOutputSet can be called by classes inheriting from the class where it's declared or by classes belonging to the same package. This means you can call it from within LookupTransformation.
However, you are trying to call it from an unrelated class - Test - located in a different package, which requires public access.
See additional explanation here.
Your code is not in a subclass (you're in Test), and your code is not in the
same package (you're in Y). So the method is not visible. That's normal.
protected means you may call the method in any derived class. However, Test isn't derived from Transformation. genOutputSet is only visible inside Transformation and LookupTransformation. This doesn't tell anything about the visibility of methods when they are called on an object of the derived class.
The best possible answer I could give would be in the form of this picture that I used to learn it myself:
Protected methods work on subclasses(inherited classes in your case) that are in other packages aswell. You are however calling it from a different class(not subclass). Hope this helps!
Related
Look, PlanetShape extends Shape!
And yet it says isOverlapMethodLevel have protected access in Shape!
Did I just discovered a bug in android studio? Or is it something else?
The packages differ in your class hierarachy - specifically your PlanetShape class is in a different package to the classes it extends.
Directly from Java documentation: (emphasis mine)
The protected modifier specifies that the member can only be accessed
within its own package (as with package-private) and, in addition, by
a subclass of its class in another package.
Clearly the "by a subclass of its class in another package" seems to be the cause of some confusion and I agree that the wording is rather ambiguous.
Basically, what they're trying to say is you can do this:
public class PlanetShape extends Shape {
aMethod() {
// call the protected method declared in the Super class (ok)
doProtectedMethodOfShape();
}
}
But not this:
public class PlanetShape extends Shape {
aMethod() {
Shape s = new Shape();
s.doProtectedMethodOfShape(); // error here
}
}
In the second example, you get an error because you're not accessing a protected method via inheritance, you're just trying to access a protected member function from an instance in another package.
I've got some doubts regarding protected identifier. In the first chapter of Sun Certified Java Programmer Study Guide by K.Sierra I found the following information:
"Once the subclass-outside-the-package inherits the protected member, that member (as inherited by the subclass) becomes private to any code outside the subclass, with the exception of subclasses of the subclass."
I provided sample code which reflects the above statement and it is absolutely clear to me.
// Parent class
package package1;
import package2.Child;
public class Parent {
protected int i = 5;
}
// Child class
package package2;
import package1.Parent;
public class Child extends Parent {
// variable 'i' inherited
}
package package2;
public class Neighbour {
public void protectedTesting(){
Child child = new Child();
System.out.println(child.i); // no access
}
}
I've started experimenting and made a small change - moved Neighbour to package1. And there is an access to "i" variable which is a little bit surprising for me as it is not in accordance to statement "becomes private to any code outside the subclass"
Neighbour class after change:
package package1;
import package2.Child;
public class Neighbour {
public void protectedTesting(){
Child child = new Child();
System.out.println(child.i); // access!
}
}
Please clarify it to me. Thanks.
In short, protected is package-private as well as visible to subclasses. Even the JLS is vague on this (JLS ยง6.6.2):
A protected member or constructor of an object may be accessed from outside the package in which it is declared only by code that is responsible for the implementation of that object.
It specifies that outside the package, only subclasses can access protected members. This implies that you can also access the variable within the package. It's poor wording, but true nonetheless that protected members have package-level visibility as well as subclass-level visibility.
See also:
This related question
The Java Trail for access control
And there is an access to "i" variable which is a little bit surprising for me as it is not in accordance to statement "becomes private to any code outside the subclass"
--> But you moved class Neighbour in package package1 which is true according to "Protected members can be accessed by classes in same package"
"Once the subclass-outside-the-package inherits the protected member, that member (as inherited by the subclass) becomes private to any code outside the subclass, with the exception of subclasses of the subclass."
--> Inside package it is still protected and not private for all classes within the package.
The truth is not in "Sun Certified Java Programmer Study Guide" but in the Java Language Specification
6.6.2. Details on protected Access
A protected member or constructor of an object may be accessed from
outside the package in which it is declared only by code that is
responsible for the implementation of that object.
protected visibility includes package level visibility.
Inheritance allows you to treat your Child object as an instance of Parent.
As the member i of Parent is declared in the same package, it is accessible from Neighbour.
package package1;
import package2.Child;
public class Neighbour {
public void protectedTesting() {
Parent neighboured = new Child();
System.out.println(neighboured.i); // access
}
}
On my Android project I have the main Activity called TestFIO, which is in the package org.testing.file.io.main, and I tried to keep it clear and sent all the functions I had to a new class called FileManipulator, which is located at org.testing.file.io.main.manipulator. Here is how the FileManipulator class looks like:
package org.testing.file.io.main.manipulator;
// imports here
public class FileManipulator extends TestFIO {
public String readFileFromCard(String location) {
// some code here
}
// more functions here
}
And here is an example of TestFIO:
// header with package and imports
import org.testing.file.io.main.manipulator.FileManipulator;
public class TestFIO extends ListActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final String[] fileString = readFileFromCard(Environment.getExternalStorageDirectory() + "test.txt");
}
}
The problem is that Eclipse is underlining readFileFromCard and showing the following error:
What am I doing wrong or how is the correct way to organize my code in packages?
PS: Sorry if this is a dumb question, I'm coming from iOS development.
The compile problem is because you're trying to call a method defined in a subclass from the superclass. Inheritance doesn't work that way; subclasses inherit all public and protected methods from the superclass, but superclasses don't know anything about the methods of their subclasses.
Additionally, it doesn't seem reasonable to have FileManipulator extend your Activity class. Does FileManipulator pass the "is-a" test, in other words, is it a kind of Activity? It seems more like it's a "helper" class that the Activity will use to do its work. In that case, FileManipulator should not extend TestFIO but rather be stand-alone, created by TestFIO.
I see TestFIO is parent class, FileManipulator is child class according to your code.
Then you can't call child class' method, you need a instance of FileManipulator.
Am I wrong?
I have an init method that is used and overridden through out an extensive heirarchy. Each init call however extends on the work that the previous did. So naturally, I would:
#Override public void init() {
super.init();
}
And naturally this would ensure that everything is called and instantiated. What I'm wondering is: Can I create a way to ensure that the super method was called? If all of the init's are not call, there is a break down in the obejct, so I want to throw an exception or an error if somebody forgets to call super.
TYFT ~Aedon
Rather than trying to do that -- I don't think it's achievable btw! -- how about a different approach:
abstract class Base {
public final void baseFunction() {
...
overridenFunction(); //call the function in your base class
...
}
public abstract void overridenFunction();
}
...
class Child extends Base {
public void overridenFunction() {...};
}
...
Base object = new Child();
object.baseFunction(); //this now calls your base class function and the overridenFunction in the child class!
Would that work for you?
Here's one way to raise an exception if a derived class fails to call up to the superclass:
public class Base {
private boolean called;
public Base() { // doesn't have to be the c'tor; works elsewhere as well
called = false;
init();
if (!called) {
// throw an exception
}
}
protected void init() {
called = true;
// other stuff
}
}
Android actually accomplishes this in the Activity class. I'm not sure how or whether they had to build support into the runtime for it, but I'd check out the open source code for the Activity class implementation. Specifically, in any of the lifecycle methods, you have to call the corresponding super class method before you do anything otherwise it throws SuperNotCalledException.
For instance, in onCreate(), the first thing you have to do is call super.onCreate().
I frequently like to use this solution. It wont throw a runtime error, but it will show a syntax error:
#CallSuper
public void init() {
// do stuff
}
This is a part of Android support annotations.
Make the class at the top of the inheritance tree set a flag on initialization. Then a class in the bottom of the inheritance tree can check for that flag to see if the whole tree has been traversed. I would make documentation that every child of base should include the following init code:
super.init()
if (!_baseIsInitialized) {
// throw exception or do w/e you wish
}
where base uses
_baseIsInitialized = true;
The other way around, forcing your childs to call super.init() is a lot thougher and would most likely include ugly hacks.
I don't know of any way to do this with a method.
However, note that this is exactly how constructors work. Every constructor must, directly or indirectly, call one of its superclass's constructors. This is statically guaranteed.
I note that you are writing an init method. Could you refactor so that your code uses constructors rather than init methods? That would give you this behaviour right out of the gate. Some people (eg me) prefer constructors to init methods anyway, partly for just this reason.
Note that using constructors rather than init methods might not mean using them on the class you're currently looking at - there might be a refactoring which moves the state needing initialisation out into a parallel class hierarchy which can use proper constructors.
Nowadays you can annotate your method with #CallSuper. This will Lint check that any overrides to that method calls super(). Here's an example:
#CallSuper
protected void onAfterAttached(Activity activity) {
if (activity instanceof ActivityMain) {
mainActivity = (ActivityMain) activity;
}
}
In the example above, any methods in descendant classes that override onAfterAttached but do not call super will make Lint raise an error.
In java, there's three levels of access:
Public - Open to the world
Private - Open only to the class
Protected - Open only to the class and its subclasses (inheritance).
So why does the java compiler allow this to happen?
TestBlah.java:
public class TestBlah {
public static void main(String[] args) {
Blah a = new Blah("Blah");
Bloo b = new Bloo("Bloo");
System.out.println(a.getMessage());
System.out.println(b.getMessage()); //Works
System.out.println(a.testing);
System.out.println(b.testing); //Works
}
}
Blah.java:
public class Blah {
protected String message;
public Blah(String msg) {
this.message = msg;
}
protected String getMessage(){
return(this.message);
}
}
Bloo.java:
public class Bloo extends Blah {
public Bloo(String testing) {
super(testing);
}
}
Actually it should be:
Open only to the classes on the same package the class and its subclasses (inheritance)
That's why
Because protected means subclass or other classes in the same package.
And there's actually a fourth "default" level of access, when the modifier is omitted, which provides access to other classes in the same package.
So protected is between default and public access.
To be more specific, you're expecting protected to work as it does in C++.
However, in Java, it has a different meaning. In Java, a protected method is available to the class (obviously), all the other classes in the same package and any subclasses of this class. Classes in other packages will not have access unless they subclass this original class.
See this similar question for more specific information on inheritance markers.
Personally, I almost never use protected. I develop applications rather than frameworks so I'm much more likely to define public methods, private data and, quite often, mark my whole class as final.
There are actually four levels of access: "public", "protected", "private" & default also known as package private or package protected. Default limits accessibility to the package. Default is quite useful and I use it frequently.
You're able to call b.getMessage() because b is of type Bloo, which extends Blah, and getMessage() is protected. Protected, as you mentioned, allows subclasses to access the method.
You've got the following errors, though:
Calling super() with no arguments in the Bloo constructor is an error. The compiler can't find the no-parameter Blah constructor because you defined one with a String parameter.
Calling new Blah() in TestBlah main method is an error for the same reason as above.
Referring to a.testing and b.testing is an error because you didn't define the variable testing for any class.