Factory & static factory methods in Java
Named constructors, hiding implementation classes.
Constructors with names
A static factory method is a named static method that returns an instance: List.of(...), Duration.ofSeconds(5), Optional.empty(). The name shows intent, which new can't do. Common names: of, from, valueOf, getInstance, newInstance.
var wait = Duration.ofSeconds(5);
var xs = List.of(1, 2, 3);
Optional<String> none = Optional.empty();Superpower: reuse instances
A constructor must create a new object. A factory may return an existing one. Boolean.valueOf returns the shared TRUE/FALSE, and Integer.valueOf caches values from -128 to 127. Your own types can do the same.
static Money of(long cents) {
if (cents == 0) return ZERO; // cached
return new Money(cents);
}Right at the edge
What does this print?
Integer a = Integer.valueOf(127);
Integer b = Integer.valueOf(127);
Integer c = Integer.valueOf(128);
Integer d = Integer.valueOf(128);
System.out.println(a == b);
System.out.println(c == d);true falsetrue truefalse false
Show the answer
127 is inside the cache, so a and b are the same object. 128 is just outside the default cache, so c and d are two different objects and == compares references. Lesson: compare values with equals, never ==.
Superpower: hide the real class
A factory can return any subtype, even a private one. Interfaces can't declare constructors, but they can have static methods. Users depend only on Cache, so you can swap the implementation in a future release without breaking anyone. List.of does exactly this.
interface Cache {
Object get(String key);
static Cache lru(int size) {
return new LruCache(size); // hidden
}
}The hidden class strikes back
List.of returns a hidden implementation class. What happens here?
List<String> xs = List.of("a", "b");
xs.set(0, "z");
System.out.println(xs);[z, b]Throws UnsupportedOperationExceptionCompile error
Show the answer
The declared type is plain List, so set compiles. But the hidden class behind List.of is unmodifiable, so set, add and remove all throw at runtime.
The price of factories
If a class offers only static factories and private constructors, it can't be subclassed. Effective Java lists that as a drawback, not an advantage (though it nudges you toward composition). Factories are also a bit harder to spot in docs than constructors.
API design
The JDK is full of factories: Path.of, List.copyOf, HttpClient.newHttpClient(). Library authors use them to return interfaces and keep the freedom to change implementation classes later. Asking users to call new LruCacheImpl(100) locks you in forever.
Key takeaways
- Names show intent: Duration.ofSeconds(5)
- Can reuse instances: Integer.valueOf caches -128 to 127
- Can return a hidden subtype: List.of returns a private class
- Common names: of, from, valueOf, getInstance, newInstance
💡 You order 'a latte' at the counter; you don't build the espresso machine yourself.
The Integer(int) constructor was deprecated in Java 9 and marked for removal in Java 16. The JDK itself wants you to call Integer.valueOf instead.
Practice questions
What does this print?
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
Integer c = Integer.valueOf(1000);
Integer d = Integer.valueOf(1000);
System.out.println(a == b);
System.out.println(c == d);- true true
- false false
- false true
- true false
Check your answer
true false. Integer.valueOf returns cached objects for -128 to 127, so a and b are the same object. 1000 is outside the default cache, so c and d are different objects.
What does this print?
List<Integer> xs = List.of(1, 2, 3);
xs.add(4);
System.out.println(xs);- [1, 2, 3, 4]
- Throws UnsupportedOperationException
- [1, 2, 3]
- Compile error
Check your answer
Throws UnsupportedOperationException. List.of is a static factory that returns a hidden unmodifiable implementation. The declared type is just List, so add compiles but fails at runtime.