4

I was reading this post on using Optionals for method arguments and the consensus seems to be to never use them for that purpose.

Guava Optional as method argument for optional parameters

But are there exceptions to this rule at all on the API side (not application side)? I can't think of a cleaner way to handle certain situations especially in initialization situations that may or may not use an argument parameter.

Take this enumerable I'm building that supplies four database connections our business uses. The top three don't require arguments because the DbManager factory already has the configuration information it needs. But the SQLite connection needs a URL for the SQLite database being used. Because of that, I needed to turn my Supplier into a Function<Optional<String>,DatabaseConnection>.

Is this legitimate case of using Optional as a method parameter? If not how can I make this enumerable?

private static enum DbConnectionSupplier {
    ORACLE(optArg -> DbManager.getOracleConnection()),
    MYSQL(optArg -> DbManager.getMySQLConnection()),
    TERADATA(optArg -> DbManager.getTeradataConnection()),
    SQLITE(optArg -> DbManager.getSQLiteConnection(optArg.get()));

    private final Function<Optional<String>, DatabaseConnection> supplier;
    private DbConnectionSupplier(Function<Optional<String>, DatabaseConnection> supplier) { 
        this.supplier = supplier;
    }

}
Community
  • 1
  • 1
tmn
  • 11,121
  • 15
  • 56
  • 112
  • Basically using `Optional` as a method parameter makes things clumsy for the caller. See also: http://stackoverflow.com/questions/23454952/uses-for-java8-optional/23464794#23464794 – Stuart Marks Mar 14 '15 at 16:33

1 Answers1

4

First, if you are using lambda expressions, you are compiling with Java 8 and you can use java.util.Optional, instead of Guava's.

I suppose you have the following methods:

DatabaseConnection getConnection(String arg) {
   return supplier.apply(Optional.of(arg));
}

DatabaseConnection getConnection() {
   return supplier.apply(Optional.empty());
}

Now, it doesn't protect you from someone calling TERADATA.getConnection("BAR"). On the other hand, SQLITE.getConnection() will fail with NoSuchElementException(Java8) or IllegalStateException(Guava) at optArg.get(), because the optional is empty. It is not clear from that exception that an argument was expected in getConnection (well, it may be clear, but the stacktrace will be "polluted" with the invocation of Function.apply)

The following implementation avoids the use of Optional, and provides clear indication of whether an argument was or was not expected.

TERADATA(()-> DbManager.getTeradataConnection()),
SQLITE(arg -> DbManager.getSQLiteConnection(arg)),
FOO(()-> DbManager.getFooConnection(),
    arg->DbManager.getFooConnection(arg));
//Foo supports both styles: with and without a parameter

private final Supplier<DatabaseConnection> supplier;
private final Function<String, DatabaseConnection> function;

private DbConnectionSupplier(Supplier<DatabaseConnection> supplier) { 
    this.supplier = supplier;
    this.function = null;
}    

private DbConnectionSupplier(Function<String, DatabaseConnection> function) { 
    this.function = function;
    this.supplier = null;
}    `

private DbConnectionSupplier(
    Supplier<DatabaseConnection> supplier,
    Function<String, DatabaseConnection> function) { 
    this.function = function;
    this.supplier = supplier;
}    

public DatabaseConnection getConnection(String arg) {
   if (function==null) throw ...
   return function.apply(arg);
}

public DatabaseConnection getConnection() {
   if (supplier==null) throw ...
   return supplier.get();
}
Javier
  • 12,100
  • 5
  • 46
  • 57