Is it possible to somehow mark certain enum
values in Java as package-private, i.e. give them the default modifier?
Background (only to preempt the otherwise immediate first comment "What for?" ;) )
I have a Task
-object with different execution-methods and an execution-state that decides which method to call next. Each one of the execution-methods returns the execution-state of the next method to be called (basically a framework for executing a state-machine).
I have an enum
that contains all possible execution-states, but also contains a few "package-internal" states like "pending" or "failed" that should not be returnable by the execution-methods.
I know I could manage these states in a separate variable with its own enum, but that would make the code a lot less clean as it turns a single switch
-statement into (at least) two (and possibly a surrounding if
). Also, I could, of course, just check the return value, but I'd rather not even make wrong ones available in the first place.
Enum constructors are always private. We can't create instance of enum using new operator. We can declare abstract methods in java enum, then all the enum fields must implement the abstract method.
Enum constructors must be private (to prevent people creating extra constants) and are private implicitly (JLS §8.9. 2).
A Public procedure is visible to all modules in a project, while a Private Enum type is not visible outside its own module.
Overview. The Java enum type provides a language-supported way to create and use constant values. By defining a finite set of values, the enum is more type safe than constant literal variables like String or int.
Sounds like the simple answer is "No."
But, thinking about the different comments and answers (particularly by Marcelo, BlackVegetable and OldCurmudgeon), I have come up with the following workaround:
A package-private enum contains all values:
enum PackagePrivateEnum {
PUBLIC_VALUE_1,
PUBLIC_VALUE_2,
PUBLIC_VALUE_3,
PACKAGE_PRIVATE_VALUE_1,
PACKAGE_PRIVATE_VALUE_2;
}
A second public enum contains only the public values, and directly maps these to the package-private ones:
public enum PublicEnum {
PUBLIC_VALUE_1 (PackagePrivateEnum.PUBLIC_VALUE_1),
PUBLIC_VALUE_2 (PackagePrivateEnum.PUBLIC_VALUE_2),
PUBLIC_VALUE_3 (PackagePrivateEnum.PUBLIC_VALUE_3);
final PackagePrivateEnum value;
private PublicEnum(PackagePrivateEnum value) {
this.value = value;
}
}
Now, if I have a function that is only allowed to return one of the public values, I define it as:
public abstract PublicEnum returnSomething();
and can then use it in the package via:
PackagePrivateEnum value = returnSomething().value;
This hides the unwanted values from the public and, I believe, simultaneously minimizes coding- and performance-overhead inside the package (e.g. no switch- or if-statements, no Map-lookups, etc., just a .value
required). In fact, with a smart compiler like GWT, the return-value should probably get "inlined" to the point that even the .value
-lookup is removed completely, i.e. no performance-overhead at all.
Also, with this, it is possible to define an arbitrary number of different allowed subsets of a big collective enum for different contexts: I could easily define another PublicEnum2
that exposes an entirely different set of values from the PackagePrivateEnum
.
You are having difficulty because you are using the wrong pattern.
Your Task
s should not return the next state. You should use a matrix of State
s to control the flow. This way your flow is not tangled up inside the tasks and the State
s remain private to the flow system.
If you want your Task
to control the flow they should return something (perhaps success/failure) to influence the flow controller. They should not define the next state, they should influence the next state.
Added
Here's a slightly contrived example of what I mean. Notice how the Task
s are attached to each State
and the flow is controlled by a Map
that merely holds each state transition.
I have made a token effort to match your returning results but I suspect that just overcomplicates matters and once you embrace the separation of flow from state you will realise what I am trying to explain.
public class Test {
public void test() {
new Thread(new Engine()).start();
}
static final Map<State, State> flow = new HashMap<>();
static {
flow.put(State.Start, State.A);
flow.put(State.A, State.B);
flow.put(State.B, State.Finished);
}
public static class Engine implements Runnable {
State state = State.Start;
@Override
public void run() {
while (state != State.Finished) {
System.out.println("State: "+state);
// Perform all tasks of this state.
for ( Task task : state.tasks ) {
System.out.println("Task: "+task);
Result result = Result.Start;
// Keep performing until completed.
while ( result != Result.Completed ) {
System.out.println("Result: "+result);
result = result.perform(task);
}
System.out.println("Result: "+result);
}
// All tasks performed! Next state.
state = flow.get(state);
}
System.out.println("State: "+state);
}
}
enum State {
Start,
A(Task.One, Task.Two),
B(Task.Two),
Finished;
Iterable<Task> tasks;
State(Task... tasks) {
this.tasks = Arrays.asList(tasks);
}
}
enum Result {
Start {
@Override
Result perform(Task t) {
return t.initialise();
}
},
Executing {
@Override
Result perform(Task t) {
return t.execute();
}
},
Finalising {
@Override
Result perform(Task t) {
return t.finalise();
}
},
Completed {
@Override
Result perform(Task t) {
// Stop there.
return Completed;
}
};
abstract Result perform(Task t);
}
enum Task {
One {
@Override
Result initialise() {
return Result.Executing;
}
@Override
Result execute() {
return Result.Finalising;
}
@Override
Result finalise() {
return Result.Completed;
}
},
Two {
@Override
Result initialise() {
return Result.Executing;
}
@Override
Result execute() {
return Result.Finalising;
}
@Override
Result finalise() {
return Result.Completed;
}
};
abstract Result initialise();
abstract Result execute();
abstract Result finalise();
}
public static void main(String args[]) {
try {
new Test().test();
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
}
Added
Simplifying this by removing your requirement to control the flow through the results of the task methods we get:
public class Test {
public void test() {
new Thread(new Engine()).start();
}
static final Map<State, State> flow = new HashMap<>();
static {
flow.put(State.Start, State.A);
flow.put(State.A, State.B);
flow.put(State.B, State.Finished);
}
public static class Engine implements Runnable {
State state = State.Start;
@Override
public void run() {
while (state != State.Finished) {
System.out.println("State: "+state);
// Perform all tasks of this state.
for ( Task task : state.tasks ) {
System.out.println("Task: "+task);
task.initialise();
task.execute();
task.finalise();
}
// All tasks performed! Next state.
state = flow.get(state);
}
System.out.println("State: "+state);
}
}
enum State {
Start,
A(Task.One, Task.Two),
B(Task.Two),
Finished;
Iterable<Task> tasks;
State(Task... tasks) {
this.tasks = Arrays.asList(tasks);
}
}
enum Task {
One {
@Override
void execute() {
}
},
Two {
@Override
void execute() {
}
};
// Nothing by default.
void initialise() {
}
abstract void execute();
// Nothing by default.
void finalise() {
}
}
public static void main(String args[]) {
try {
new Test().test();
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
}
which, I think, demonstrates the separation of flow control from task execution I was trying to get across.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With