2

Lately I've started adding this to certain classes in an in-house API:

public class MyClass {

  // I usually do this with classes I expect to 
  // be printed out or tested a lot (particularly
  // in the early stages of creating the class/package).
  // 
  // Having one field is not a criteria for doing this,
  // just a simple example.  And of course there is always
  // a default value for the toString field.

  private Object oneOtherFieldExample;

  private Function<MyClass, String> toString = 
      myClass -> String.valueOf(oneOtherFieldExample);

  ...

  public void setToString(Function<MyClass,String> toStringFunction) {
    toString = toStringFunction;
  }

  public String toString() {
    return toString.apply(this);
  }

This has come in handy a couple times but since I'm working on an in-house API I'm wondering

  • Do you all foresee any potential issues I'm missing?
  • Is this reasonably legible and intuitive?
  • Does this or something similar already exist as some kind of pattern (and if so, what is it called)?
  • Since this is especially used for testing and early building, should I add a static way to set the default toString variable to avoid having to set it manually every time I instantiate a new MyClass Object (in the case where thousands are being created)?
4
  • 3
    What problem are you actually trying to solve? Why aren't you just doing a 'Source > Generate toString()' in Eclipse (or whatever IDE)? – user40980 Nov 19 '15 at 17:23
  • I use source>generate in Eclipse usually, but this came up when creating a smaller class I typically have large collections of and am often printing them (using something like ...stream().peek(System.out::println)...). Then it just occurred to me that since it's part of an API, an easy way to modify how/what it prints when testing could be helpful. – roundar Nov 19 '15 at 17:29
  • Have you looked at how Eclipse handles printing collections in its generated toString()? To me it really feels like this is just some clever code around toString() that doesn't solve any problem in an a more understandable way. – user40980 Nov 19 '15 at 17:31
  • I'm familiar with how Eclipse prints collections (I'm not really talking about printing collections, I'm talking about long processes that print elements of a collection as they go). The only comparable/relevant alternative to this would be something like new MyClass() { @Override public void toString() { ... } }; Which wouldn't be worth doing, but if you could create a few variables with different printing methods for different scenarios you could just: if(something) mc.setToString(functionA); else mc.setToString(functionB); – roundar Nov 19 '15 at 17:39
3

First-Class functions and Higher Order Functions

A programming language has first-class functions if it supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures.

In languages where functions are first-class citizens, functions can be passed as arguments to other functions in the same way as other values (a function taking another function as argument is called a higher-order function).

It's a perfectly valid, well-respected technique, especially among the Functional Programming crowd. As you've already discovered, it allows you to change the behavior of a class without changing the class itself.

The canonical example of higher-order functions is Map. The Map function accepts a function as a parameter and iterates over a list, applying the function to each item in the list.

void map(int (*f)(int), int x[], size_t n) {
    for (int i = 0; i < n; i++)
        x[i] = f(x[i]);
}

Another example of higher-order functions is a comparator. Comparators are used in sort functions to define how the sort compares one value in the collection to another value for ordering purposes.

var result = MergeSort(listOfImaginaryNumbers, ImaginaryNumberComparator);

Is this a viable technique for your scenario?

To be fair, any given class should already know how to ToString() itself, without any help from the outside world. If I were designing such a class, and I needed some variation on the ToString() output, I would probably provide some parameter in the constructor that the ToString() method could use to modify its behavior, rather than resorting to first-class functions. Or I would examine the state of the object to decide how ToString() should behave. To be genuinely useful, the first-class function would have to have knowledge of your class's internals, which would violate encapsulation.

Depending on exactly what you want to accomplish, a Map function might be a suitable alternative.

Further Reading
First-Class Functions

0
2

In general, one would expect the toString() method to be deterministic. Invoking it multiple times on the same object without changing any object state should produce the same results. While not a part of the general contract for toString(), many implementations of libraries and frameworks I have worked with expect this.

By using a lambda or other functional approach to "reach in" and change the output you violate that unwritten expectation.

It would be better to use a decorator or a variant on it to alter the behavior.

How this works is your core object remains the same. It always produces the same toString() output.

If you need different output, construct a different object containing the first one as a parameter. Calling toString() on this outer object produces different results.

Consider the following example:

public interface Widget { ... }

public class MyWidget implements Widget { ... }

public class HtmlWidget implements Widget {
  public HtmlWidget(Widget w) { ... }
}

public class CsvWidget implements Widget {
  public CsvWidget(Widget w) { ... }
}

The class MyWidget will truck along as it always has, having a basic toString() implementation. The HtmlWidget and CsvWidget classes wrap another widget. They pass through all of the method calls to the underlying object except for toString(): they output a Widget as (HTML and CSS) or CSV, respectively.

They key benefit here is separation of concerns: each object is responsible for one concern. The Widget does Widget things, the decorator Widgets decorate.

Assuming the underlying data is stable, you can even have multiple threads to-stringing the same object using different decorators at the same time, because the original object is unchanged.

4
  • A Decorator? You would create a hierarchy of classes just to modify the behavior of a ToString() function? – Robert Harvey Nov 19 '15 at 19:36
  • @RobertHarvey if it is important enough to have a lambda, why not? I have done this for top-level objects that need to be serialized in different ways. It is not appropriate for all scenarios but is worth considering. The benefit is it adheres to SRP by separating out the complex serialization logic (which is what toString() is in this case) to dedicated objects. – user22815 Nov 19 '15 at 20:47
  • I don't think it's important enough for a lambda, but I could be wrong. – Robert Harvey Nov 19 '15 at 21:42
  • 1
    I've seen this technique used to great effect, though not as full decorators but only as a limited view on one aspect: (1) as frontends to a serialization mechanism: new JsonSerialization(myObject) would have a toString() producing JSON, while new XmlSerialization(myObject) would produce XML. (2) to add debug info into toString by a wrapper new Dump<MyClass>(myObject) type, though that was more useful with C++'s template programming. In Java, we could have an interface Dumpable { public String dump(); } that a class Dump<T extends Dumpable> could delegate to. – amon Nov 22 '15 at 11:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.