A class can provide a public static factory method to return a instance of itself.
The simple way to see it: It just a method that returns an object, nothing special. How to implement the method is controlled by us. We can return the same cached object every time; we can also give back brand new object; we are free to choose any subclass object of the return type; we can add some ingenuity in the process of building and selecting the object.
Advantage:
When a class has overloaded constructors, we can replace the multiple constructors with static factory methods, and choose names to highlight their differences.
- This technique can also be used to solve the problem that constructors have the same parameter types.
public class Complex { private final float re; private final float im; private Complex(float re, float im) { this.re = re; this.im = im; } public static Complex valueOf(float re, float im) { return new Complex(re, im); } public static Complex valueOfPolar(float r, float theta) { return new Complex((float) (r * Math.cos(theta)), (float) (r * Math.sin(theta))); }
- This technique can also be used to solve the problem that constructors have the same parameter types.
Static factory methods are not required to create a new object each time they’re invoked.
- Static factory methods have the ability to return the same object from repeated invocations
- Used in Singleton Pattern, Immutable class, instance-controlled classes
public static Boolean valueOf (boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; }
Static Factory methods can return an object of any subtype of their return type.
- Help for hiding implementation classes. This technique is used in building interface-based frameworks.
- In java Collections framework. There are 32 convenience implementation of the Collection interface. Nearly all of these implementations are exported via static factory methods in the noninstantiable class (java.util.Collections)
java.util.Collections Source Code
public class Collections { // Suppresses default constructor, ensuring non-instantiability. private Collections() { } public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) { return new UnmodifiableCollection<T>(c); } /** * @serial include */ static class UnmodifiableCollection<E> implements Collection<E>, Serializable {} public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) { return new UnmodifiableMap<K,V>(m); } /** * @serial include */ private static class UnmodifiableMap<K,V> implements Map<K,V>, Serializable {} }
Using such a static factory method requires the client to refer to the returned object by its interface rather than its implementation class, which is generally good practice.
- The class of the returned object can vary from invocation to invocation depending on the values of the parameters to the static factory. The implementation can be easily swapped.
- The class of the returned object need not even exist at the time the static factory method is written.
Like in the Service Provider framework: in which multiple service providers implement a service, and the framework makes the implementations available to its clients.
//Service interface public interface Service { } //Service provider interface public interface Provider { public abstract Service newSerive(); } //Noneinstantiable class for service registration and access public class Services { private Services(){} //Map service names to service providers private static final Map<String, Provider> providers = new ConcurrentHashMap<>(); public static final String DEFAUL_PROVIDER_NAME = "<def>"; //Provider registration API pubilc static void registerDefaultProvider(Provider p) { registerProvider(DEFAUL_PROVIDER_NAME, p); } public registerProvider(String name, Provider p) { providers.put(name, p); } //Service access API pubilc static Service getService() { return getService(DEFAUL_PROVIDER_NAME ); } public static Service getService(String name) { Provider p = providers.get(name); if (p == null) { throw new IllegalArgumentException("No provider registered with name: " + name); } return p.newService(); } }
Similar to Strategy Pattern