I currently am publishing a library for using swing with Processing. I declared almost everything in my library public with the motivation that if someone knows what they’re doing one can use these methods. However this has the effect that the javadoc is full of a few methods that aren’t supposed to be used when you don’t know what you’re doing.
Should I reduce the visibility of some methods so you need to access them with reflect or should I try to make clear they aren’t supposed to be used often?
I’d just publish it unabridged and let the end user make the decision to use them or not.
Excellent! I hate the private keyword.
Please don’t! Reflect is such a slow boilerplate!
Actually there are 2 solutions for that!
Easiest 1 is to use comment tag @hidden just before a member you want excluded from javadoc:
docs.Oracle.com/en/java/javase/11/docs/specs/doc-comment-spec.html#hidden
This way you can still have public members w/o cluttering your generated doc.
However notice tag @hidden was introduced in JDK 9.
So if you’re using JDK 8 to compile your library you still need a later version just to run javadoc.
- The other approach is to restrict members w/ keyword
protected. - A
protectedmember behaves similarly toprivaterestriction. - But it opens up an access exception inside another class which
extendsits class. - We can also access
protectedmembers inside a file w/ the samepackagename as theirs. - Otherwise they behave as
privatemembers, not allowing direct access unless using reflection. - However javadoc includes both
public&protectedmembers by default. - But it’s very easy to get around it by passing parameter “-public” in order to exclude
protectedmembers from its generated doc. - Also you can use both strategies, declaring less restricted members w/ comment tag
@hiddenand more restricted members w/ keywordprotectedin place ofprivate. - But there’s still another caveat when using the
protectedkeyword. - If some class happens to instantiate another class which has
protectedmembers in it, it’s always gonna do it on the original class, not from a user’s subclass. - That is, even if a user
extendsa class in order to access/modify itsprotectedmembers, the user can’t force the rest of your library to use that modified subclass. - In such cases you should create an extra call sig which allows a user to pass their subclass to it.
As an example, let’s say you have a class A which instantiates inside its constructor a class B:
“A.java”:
package pseudo.privated.members;
public class A {
/**
* @hidden
*/
public B b;
public A() {
b = new B();
}
}
However that class B contains a protected method called protectedMethod():
“B.java”:
package pseudo.privated.members;
public class B {
protected String protectedMethod() {
return "protected: " + getClass().getSimpleName();
}
}
If a user desires to subclass class B in order to modify protectedMethod() it’s gonna be in vain, b/c your class A will always instantiate the original class B, never some subclass of it.
As a workaround the user could later reassign field b from class A w/ a modified class B of theirs.
However that’d feel like a poor hack, given field A::b is @hidden from javadoc.
A more streamlined solution would be to offer an alternative constructor signature which accepts a B subclass from the user:
“A.java”:
package pseudo.privated.members;
public class A {
/**
* @hidden
*/
public B b;
public A() {
this(null);
}
public A(final B subclass) {
b = subclass == null? new B() : subclass;
}
}
A regular user would simply instantiate class A using its 1st call signature.
On the other hand, an expert user would know how to use its 2nd signature.
Regardless, either class A instantiates its own class B or use the 1 passed to it.
You could even tag the alternative signature w/ @hidden so only experts would be aware of it:
/**
* @hidden
*/
public A(final B subclass) {
b = subclass == null? new B() : subclass;
}
Now I’m gonna leave you w/ my complete “Public Hidden Members” sketch example:
“Public_Hidden_Members.pde”:
/**
* Public Hidden Members (v1.0.0)
* GoToLoop (2022/May/27)
*
* https://Discourse.Processing.org/t/
* best-practice-regarding-visibility/37113/4
*/
import pseudo.privated.members.A;
A a;
void setup() {
println(a = new A()); // protected: B
println(a.b.hiddenMethod()); // hidden: B
println(a = new A(new BB())); // subclassed: BB
println(a.b.hiddenMethod()); // hidden: BB
exit();
}
import pseudo.privated.members.B;
class BB extends B {
@Override String protectedMethod() {
return "subclassed: " + getClass().getSimpleName();
}
}
“A.java”:
package pseudo.privated.members;
public class A {
/**
* @hidden
*/
public B b;
public A() {
this(null);
}
/**
* @hidden
*/
public A(final B subclass) {
b = subclass == null? new B() : subclass;
}
@Override public String toString() {
return b.protectedMethod();
}
}
“B.java”:
package pseudo.privated.members;
public class B {
/**
* @hidden
*/
public String hiddenMethod() {
return "hidden: " + getClass().getSimpleName();
}
protected String protectedMethod() {
return "protected: " + getClass().getSimpleName();
}
}
That’s nice but the problem is that you have no fine control over what the user might update / override.
Using private, protected and final access specifiers do that and more. They help
- users avoid mistakes using the library
- the library creator trying to fix user errors when using parts of the library they shouldn’t
- prevent the user bypassing any validation that you might want to apply.
Here is an example of what I mean.
Start with our library class
package forum; // would be a library package name
public class LibraryClass {
protected int n = 0;
public LibraryClass() { }
public LibraryClass(int n) {
this.n = getValidN(n);
}
// private validation :- change number to positive
private int getValidN(int n) {
return Math.abs(n);
}
public void n(int n) {
this.n = getValidN(n);
}
public int n() {
return this.n;
}
public String toString() {
return this.getClass().getSimpleName()+ " { n = " + n + " }";
}
}
We can see that the attribute n can only be positive. What if we wanted to modify the behaviour so n can only be negative? Under no circumstances do we want to modify the library because it would adversely affect existing code that used the library. The answer is for the user to inherit from the LibraryClass and override the private validation method like this
public class UserClass extends LibraryClass {
public UserClass(int n) {
super();
this.n = getValidN(n);
}
// private validation :- change number to negative
private int getValidN(int n) {
return -Math.abs(n);
}
// required to enable to ensure correct validity chack is applied
public void n(int n) {
this.n = getValidN(n);
}
public String toString() {
return this.getClass().getSimpleName()+ " { n = " + n + " }";
}
}
So in Processing we have
void setup() {
LibraryClass lc = new LibraryClass(-42);
println(lc);
LibraryClass uc = new UserClass(42);
println(uc);
}
Which produces the output
LibraryClass { n = 42 }
UserClass { n = -42 }
So we have an autonomous library class which we can inherit from to override private access methods.
In other words we have an extensible, secure library which is surely a good thing ![]()