Static factory method is a static method that returns an instance of a class. The key idea is to gain control over object creation and delegate it from constructor to static method.
The key idea of static factory method is to gain control over object creation and delegate it from constructor to static method. The decision of object to be created is like in Factory made outside the method (in common case, but not always). While the key (!) idea of Factory Method is to delegate decision of what instance of class to create inside Factory Method. E.g. classic Singleton implementation is a special case of static factory method. Example of commonly used static factory methods:
- valueOf
- getInstance(used in singleton)
- newInstance
When to use?
- Static factory methods can have meaningful names, hence explicitly conveying what they do
- Static factory methods can return the same type that implements the method(s), a subtype, and also primitives, so they offer a more flexible range of returning types
- Static factory methods can encapsulate all the logic required for pre-constructing fully initialized instances, so they can be used for moving this additional logic out of constructors. This prevents constructors from performing further tasks, others than just initializing fields
Another example of static factory is as follow
Optional<String> value1 = Optional.empty(); Optional<String> value2 = Optional.of("Baeldung"); Optional<String> value3 = Optional.ofNullable(null);
Logger.java
public class Logger { public String logType; public String fileLocation; public Logger(String logType) { this.logType = logType; } public Logger() { } public static Logger getDefaultLogger(){ return new Logger("Console"); } public static Logger getFileLogger(String fileLocation){ Logger logger = new Logger("File"); logger.fileLocation = fileLocation; return logger; } @Override public String toString() { return "Logger{" + "logType=" + logType + ", fileLocation='" + fileLocation + '\'' + '}'; } }
ClientApp.java
public class ClientApp { public static void main(String[] args) { Logger objLogger = Logger.getDefaultLogger(); System.out.println(objLogger); } }
Output
Logger{logType=Console, fileLocation='null'}
Logger.java
public class Logger { public String logType; public String fileLocation; public Logger(String logType) { this.logType = logType; } public Logger() { } public static Logger getLoggerInstance(LoggerType loggerType){ Logger logger; switch(loggerType) { case CONSOLE: logger = new Logger("Console Logger"); logger.fileLocation = "JVM Memory"; break; case DATABASE: logger = new Logger("Database Logger"); logger.fileLocation = "DB Connection"; break; case FILE: logger = new Logger("File Logger"); logger.fileLocation = "C:/logs"; break; case SPLUNK: logger = new Logger("Splunk Logger"); logger.fileLocation = "Splunk URL"; break; default: logger = new Logger(); } return logger; } @Override public String toString() { return "Logger{" + "logType=" + logType + ", fileLocation='" + fileLocation + '\'' + '}'; } }
ClientApp.java
public static void main(String[] args) { Logger objLogger = Logger.getLoggerInstance(LoggerType.CONSOLE); System.out.println(objLogger); }
LoggerType.java
public enum LoggerType { CONSOLE, FILE, DATABASE, SPLUNK }
Output
Logger{logType=Console Logger, fileLocation='JVM Memory'}