My solution was to create a sealed interface that permitted the None and Some records as the only classes to implement it. Those records are not available outside the package, while the interface is exposed. Using default methods in the interface I could expose a state "create()" method which would then instantiate the appropriate None or Some record. In this way you control the exposure of the construction of the specific record implementations of your interface.
You can then either interact with the option through the methods on the interface, .isOk(), .unwrap(), etc, etc, or with the upgraded pattern matching in switches with this release you could have something like
switch(option) {
case option when option.isNone() -> blah
case option when option.isSome() -> foo
}Its not as pleasing as Rust matching directly on Some and None, but it gets you pretty close.