ProxyInvocationHandler implements InvocationHandler, Serializable { @Override public synchronized String toString() { SortedMap<String, Object> sortedOptions = new TreeMap<>(); sortedOptions.putAll(jsonOptions); for (Map.Entry<String, BoundValue> entry : options.entrySet()) { sortedOptions.put(entry.getKey(), entry.getValue().getValue()); } StringBuilder b = new StringBuilder(); b.append("Current Settings:\n"); for (Map.Entry<String, Object> entry : sortedOptions.entrySet()) { b.append(" " + entry.getKey() + ": " + entry.getValue() + "\n"); } return b.toString(); } ProxyInvocationHandler(Map<String, Object> options); private  ProxyInvocationHandler(
      Map<String, BoundValue> options, Map<String, JsonNode> jsonOptions); @Override Object invoke(Object proxy, Method method, Object[] args); String getOptionName(Method method); @Override boolean equals(@Nullable Object obj); @Override int hashCode(); Map<String, Map<String, Object>> outputRuntimeOptions(PipelineOptions options); @Override synchronized String toString();  }
@Test public void testToString() throws Exception { assumeFalse(SystemUtils.IS_OS_WINDOWS); ProxyInvocationHandler handler = new ProxyInvocationHandler(Maps.newHashMap()); StringWithDefault proxy = handler.as(StringWithDefault.class); proxy.setString("stringValue"); DefaultAnnotations proxy2 = proxy.as(DefaultAnnotations.class); proxy2.setLong(57L); assertEquals( String.format("Current Settings:%n" + " long: 57%n" + " string: stringValue%n"), proxy.toString()); }