code: public Interval getInstance(Long start, Granularity startGran,
              Long finish, Granularity finishGran) {
          List<Object> key = Arrays.asList(new Object[]{start, startGran,
              finish, finishGran});
          Interval result;
          synchronized (cache) {
              result = cache.get(key);
              if (result == null) {
                  if (start == null || finish == null) {
                      result = new DefaultInterval(start, startGran, finish, finishGran);
                  } else {
                      result = new SimpleInterval(start, startGran, finish, finishGran);
                  }
                  cache.put(key, result);
              }
          }
          return result;
      }
docs: Returns at interval specified by the given start and finish and
  granularities.
  
  @param start a {@link Long} representing the start of the interval. If
  <code>null</code>, the <code>start</code> will be unbounded.
  @param startGran the {@link Granularity} of the start of the interval.
  The <code>start</code> parameter's interpretation depends on the
  {@link Granularity} implementation provided.
  @param finish a {@link Long} representing the finish of the interval. If
  <code>null</code>, the <code>finish</code> will be unbounded.
  @param finishGran the {@link Granularity} of the finish of the interval.
  The <code>start</code> parameter's interpretation depends on the
  {@link Granularity} implementation provided.
  @return an {@link Interval}.