Skip to content

Instantly share code, notes, and snippets.

@munyari
Last active October 6, 2016 17:27
Show Gist options
  • Save munyari/b80ae382b07157b94e158d316a0832c4 to your computer and use it in GitHub Desktop.
Save munyari/b80ae382b07157b94e158d316a0832c4 to your computer and use it in GitHub Desktop.
layout title date tags
post
The Flyweight Pattern in Java
2016-10-06 09:23:13 -0400
programming java

Types in Java fall into two categories, the primitive types (char, boolean, byte, short, int, long, float, double) and the reference types (objects and arrays). Each primitive type has a corresponding reference type, often called a boxed primitive, which allow these types to be used anywhere an object would be required, as well as adding some useful methods and fields. Conversion from a primitive to a boxed primitive is called boxing and the converse is called unboxing. Objects are typically created by invoking a constructor, or using a static factory method. One of the major differences between these two techniques is that a constructor requires a new object's reference to be returned[^citation?], while a factory method would merely have to return an object of that type. This means that the same object can be returned on multiple invocations when using a factory method.

As a result static factory methods facilitate the implementation of the flyweight pattern. In the case of an immutable class, if some values are requested much more frequently than others, it may make sense from a performance and memory perspective for the class to keep a cache of the most commonly requested values, instead of creating a new copy wherever one is requested. The Java Language Specification (JLS) gives us a simple case-study of this pattern in use. According to the JLS, if p is an int in \([-128, 127]\) it is guaranteed that two boxing operations on p will return the same reference. In other words, the Integer class must cache all of the values in \([-128, 127]\)1.

On my laptop (openjdk 8.u102-1)2, running

public class Flyweight
{
  public static void main(String[] args)
  {
    System.out.println(Integer.valueOf(127) == Integer.valueOf(127));
    System.out.println(Integer.valueOf(128) == Integer.valueOf(128));
    System.out.println(new Integer(127) == new Integer(127));
  }
}

prints

true
false
false

As discussed above, explicitly invoking the constructor twice will create two new objects. Since == on objects tests equality of reference on objects, this will fail. The first example shows that Integer.valueOf(127) will always return the same reference. Relying on this behavior may result in subtle bugs: == may work in the most common cases and fail in rarer cases. If my application uses the first 127 integers often, I may not notice this at all until one day I have a perplexing error where == does not work properly. As a rule of thumb, when testing object equality == will rarely do want you want; use the equals() method instead.

Some takeaways:

  • Don't use == comparison on objects. It could potentially lead to a subtle bug. Instead use Object.equals().
  • Consider using valueOf or other static factories instead of constructors to take advantage of caching. This can positively impact your applications performance.3
  • Don't use the boxed primitive constructors. 4

Footnotes

  1. there are similar rules for Character and Boolean

  2. Note that this behavior is JVM specific, as a JVM is only required to cache the values in \([-128, 127]\), but may cache more.

  3. There are other advantages of using static factories over constructors, see Effective Java Item (something) for a discussion of the trade-offs.

  4. The constructors of the boxed primitives are deprecated in Java 9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment