java static factory methods
TRANSCRIPT
JAVACONSIDER STATIC FACTORY METHODS INSTEAD OF CONSTRUCTORS
Ye Win11/21/2015
Contents• Preface (Summary)
• Static Factory Methods
• Advantages
• Disadvantages
• Summary (Preface)
Preface (Summary)In preface, static factory methods and public constructors both have their uses, and it pays to understand their relative merits. Often static factories are preferable, so avoid the reflex to provide public constructors without first considering static factories.
Static Factory Methods• The normal way for a class to allow a client to obtain an instance of
itself is to provide a public constructor. There is another technique that should be a part of every programmer’s toolkit.
• A class can provide a public static factory method, which is simply a static method that returns an instance of the class.
Static Factory MethodsHere’s a simple example from Boolean (the boxed primitive class for the primitive type boolean). This method translates a boolean primitive value into a Boolean object reference:
public static Boolean valueOf(boolean b) {return b ? Boolean.TRUE : Boolean.FALSE;
}
A class can provide its clients with static factory methods instead of, or in addition to, constructors. Providing a static factory method instead of a public constructor has both advantages and disadvantages.
AdvantagesFirst advantage of static factory methods is that, unlike constructors, they have names.
• If the parameters to a constructor do not, in and of themselves, describe the object being returned, a static factory with a well-chosen name is easier to use and the resulting client code easier to read.
• Now let’s seen by an example.
Advantages• Look at the following example.
• We have a RandomIntGenerator class that, as the name suggests, generates random int numbers. Something like:
public class RandomIntGenerator { private final int min; private final int max;
public int next() {...} }
Advantages• Our generator takes a minimum and maximum and then generates
random numbers between those 2 values. Notice that the two attributes are declared final so we have to initialize them either on their declaration or in the class constructor. Let’s go with the constructor:
public RandomIntGenerator(int min, int max) { this.min = min; this.max = max;
}
Advantages• Now, we also want to give our clients the possibility to specify just a
minimum value and then generate random values between that minimum and the max possible value for ints. So we add a second constructor:
public RandomIntGenerator(int min) { this.min = min; this.max = Integer.MAX_VALUE;
}
Advantages• So far so good, right? But in the same way that we provided a
constructor to just specify the minimum value, we want to do the same for just the maximum. We’ll just add a third constructor like:
public RandomIntGenerator(int max) { this.min = Integer.MIN_VALUE; this.max = max;
}
• If you try that, you’ll get a compilation error that goes: Duplicate method RandomIntGenerator(int) in type RandomIntGenerator. What’s wrong?
Advantages• Full script:public class RandomIntGenerator {
private final int min; private final int max;
public int next() {...}
public RandomIntGenerator(int min, int max) { this.min = min; this.max = max; }
public RandomIntGenerator(int min) { this.min = min; this.max = Integer.MAX_VALUE; }
/* Compilation error will be occurred. */public RandomIntGenerator(int max) { this.min = Integer.MIN_VALUE; this.max = max; }
}
Advantages• The problem is that constructors, by definition, have no names. As
such, a class can only have one constructor with a given signature in the same way that you can’t have two methods with the same signature (same return type, name and parameters type).
• That is why when we tried to add the RandomIntGenerator(int max) constructor we got that compilation error, because we already had the RandomIntGenerator(int min) one.
Advantages• Now let’s apply static factories to our RandomIntGenerator example, we could get:
public class RandomIntGenerator { private final int min; private final int max; // private constructor to non-institate class private RandomIntGenerator(int min, int max) { this.min = min; this.max = max; } public static RandomIntGenerator between(int max, int min) { return new RandomIntGenerator(min, max); } public static RandomIntGenerator biggerThan(int min) { return new RandomIntGenerator(min, Integer.MAX_VALUE); }
public static RandomIntGenerator smallerThan(int max) { return new RandomIntGenerator(Integer.MIN_VALUE, max); }
public int next() {...}}
Advantages• Note : How the constructor was made private to ensure that the
class is only instantiated through its public static factory methods.
• Also note how your intent is clearly expressed when you have a client with RandomIntGenerator.between(10,20) instead of
new RandomIntGenerator(10,20)
• Point is clear, we replace the constructors with static factory methods and carefully chosen names to highlight their differences.
AdvantagesA second advantage of static factory methods is that, unlike constructors, they are not required to create a new object each time they’re invoked.
This is extremely useful when working with immutable classes to provide constant objects for common used values and avoid creating unnecessary duplicate objects. (You can refer avoid creating uncessary objects in this link
http://www.slideshare.net/mysky14/avoid-creating-unncessary-objects)
Advantagespublic static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;}
• The Boolean.valueOf code that I showed previously illustrates this point perfectly. Notice that this static method returns either TRUE or FALSE, both immutable Boolean objects.
• This technique is similar to the Flyweight pattern (I am promise I will post about this
pattern in near future, please don’t forget to following me at slideshare.). It can greatly improve performance if equivalent objects are requested often, especially if they are expensive to create.
AdvantagesA third advantage of static factory methods is that, unlike constructors, they can return an object of any subtype of their return type.
• This gives you great flexibility in choosing the class of the returned object.
• Moreover, you can hide implementation classes and have an interface-based API (I am promise I will post about this interface-based framework in near future, please don’t forget to following
me at slideshare.), which is usually a really good idea.
• But I think this can be better seen by an example.
Advantages• Look at the following example.
• Imagine that we now want to provide random generators not just for integers but for other data-types like String, Double or Long. They are all going to have a next() method that returns a random object of a particular type, so we could start with an interface like:
public interface RandomGenerator<T> { T next();}
Advantages• Our first implementation of the RandomIntGenerator now becomes:
class RandomIntGenerator implements RandomGenerator<Integer> { private final int min; private final int max;
RandomIntGenerator(int min, int max) { this.min = min; this.max = max; }
public Integer next() {...}}
Advantages• We could also have a String generator:
class RandomStringGenerator implements RandomGenerator<String> { private final String prefix;
RandomStringGenerator(String prefix) { this.prefix = prefix; }
public String next() {...}}
Advantages• Notice how all the classes are declared package-private (default scope) and so are
their constructors. This means that no client outside of their package can create instances of these generators. So what do we do? Tip: It starts with “static” and ends with “methods”. Consider the following class:
public final class RandomGenerators { // Suppresses default constructor, ensuring non-instantiability. private RandomGenerators() {}
public static final RandomGenerator<Integer> getIntGenerator() { return new RandomIntGenerator(Integer.MIN_VALUE, Integer.MAX_VALUE); }
public static final RandomGenerator<String> getStringGenerator() { return new RandomStringGenerator(""); }}
Advantages• RandomGenerators is just a noninstantiable utility class with nothing
else than static factory methods. Being on the same package as the different generators this class can effectively access and instantiate those classes. But here comes the interesting part.
• Note that the methods only return the RandomGenerator interface, and that’s all the clients need really.
• If they get a RandomGenerator<Integer> they know that they can call next() and get a random integer.
AdvantagesNot only can the class of an object returned by a public static factory method be nonpublic, but the class can vary from invocation to invocation depending on the values of the parameters to the static factory.
Any class that is a subtype of the declared return type is permissible. The class of the returned object can also vary from release to release for enhanced software maintainability and performance.
Advantages• A fourth advantage of static factory methods is that they reduce
the verbosity of creating parameterized type instances.
• Have you ever had to write code like this?
Map<String, List<String>> map = new HashMap<String,List<String>>();
Advantages• You are repeating the same parameters twice on the same line of
code. Wouldn’t it be nice if the right side of the assign could be inferred from the left side?
• Well, with static factories it can. The following code is taken from Guava’s Maps class:
public static <K, V> HashMap<K, V> newHashMap() { return new HashMap<K, V>();}
• So now our client code becomes:
Map<String, List<String>> map = Maps.newHashMap();
Advantages• Pretty nice, isn’t it? This capability is known as Type inference. It’s
worth mentioning that Java 7 introduced type inference through the use of the diamond operator. So if you’re using Java 7 you can write the previous example as:
Map<String, List<String>> map = new HashMap<>();
• Unfortunately, the standard collection implementations such as HashMap do not have factory methods as of release 1.6, but you can put these methods in your own utility class.
• More importantly, you can provide such static factories in your own parameterized classes.
DisadvantagesThe main disadvantage of static factories is that classes without public or protected constructors cannot be extended. But this might be actually a good thing in some cases because it encourages developers to favor composition over inheritance. (I am promise I will post about this interface-based framework in near future,
please don’t forget to following me at slideshare.)
• The same is true for nonpublic classes returned by public static factories. For example, it is impossible to subclass any of the convenience implementation classes in the Collections Framework.
• But I think this can be better seen by an our own example.
Disadvantages• Suppose you have a class called Person:
class Person { public static Person createWithFirstName(String firstName) { return new Person(firstName, null, null); } // etc. - more factory methods
// private constructor private Person(String firstName, String lastName, String nickname) { }
// useful method public String getDisplayName() { }}
Disadvantages• It's all good and dandy. But now you also need a class
called Programmer, and you suddenly realize the programmers are persons too!
• But all of a sudden, you can't just
class Programmer extends Person { }
• since Person doesn't have any public constructors.
• You can’t not subclassed which are not have public or protected constructor.
Summary (Preface)• In summary, static factory methods and public constructors both have
their uses, and it pays to understand their relative merits. Often static factories are preferable, so avoid the reflex to provide public constructors without first considering static factories.
References• Effective Java – 2nd Edition (Joshua Bloch)
• http://stackoverflow.com/questions/19733789/static-factory-disadvantage-over-constructor
• https://jlordiales.wordpress.com/2012/12/26/static-factory-methods-vs-traditional-constructors/