Reputation: 5255
I have a collection of objects that all override toString()
.
I want to write them out to console or concatenate to a string, basically creating a toString()
for the collection.
I can use a for loop to achieve this. String.join()
seems to be a much nicer way though, because it takes away the explicit loop:
import java.util.ArrayList;
public class Foo
{
private double member;
public Foo()
{
member = Math.random();
}
@Override
public String toString()
{
return "I'm foo " + member;
}
public static void main(String[] args)
{
ArrayList<Foo> foos = new ArrayList<>();
foos.add(new Foo());
foos.add(new Foo());
foos.add(new Foo());
foos.add(new Foo());
foos.add(new Foo());
foos.add(new Foo());
// print collection with loop
for (Foo foo : foos)
{
System.out.println(foo);
}
// print collection with String.join
System.out.println(String.join(System.lineSeparator(), foos)); // fails
}
}
For System.out.println()
to work, the toString()
method is called. No interface has to be implemented explicitly. This seems to be easy and is a common practice.
However, to get String.join(CharSequence delimiter, Iterable<? extends CharSequence> elements)
to work, merely having a toString()
method is not enough. I need an Iterable<? extends CharSequence>
, which means Foo
should implement CharSequence
.
Now I quickly implemented that interface by delegating to toString()
:
import java.util.ArrayList;
import java.util.stream.IntStream;
import java.lang.CharSequence;
public class Foo implements CharSequence
{
private double member;
public Foo()
{
member = Math.random();
}
@Override
public char charAt(int index)
{
return toString().charAt(index);
}
@Override
public IntStream chars()
{
return toString().chars();
}
@Override
public IntStream codePoints()
{
return toString().codePoints();
}
@Override
public int length()
{
return toString().length();
}
@Override
public CharSequence subSequence(int start, int end)
{
return toString().subSequence(start, end);
}
@Override
public String toString()
{
return "I'm foo " + member;
}
public static void main(String[] args)
{
ArrayList<Foo> foos = new ArrayList<>();
foos.add(new Foo());
foos.add(new Foo());
foos.add(new Foo());
foos.add(new Foo());
foos.add(new Foo());
foos.add(new Foo());
System.out.println(String.join(System.lineSeparator(), foos));
}
}
But this seems to be quite a bit of code which isn't doing much in this situation except guaranteeing the possibility to convert the object into a String
which every Object
has anyway in the form of toString()
.
The boilerplate code introduced in the solution is bigger than the boilerplate code it removed.
How can I make a class play nice with String.join()
?
Should I go that route and implement the interface or should I run some conversion on the fly?
Upvotes: 0
Views: 2389
Reputation: 2605
Depending on the usage, that route may be inefficient, because each call to a CharSequence method invokes toString anew. For example, if a method tried to iterate over each character, you'd be looking at abysmal performance. Since you are using Java 8, why not use the Streams API?
System.out.println(foos.stream()
.map(Object::toString)
.collect(Collectors.joining(System.lineSeparator())));
Upvotes: 4