What's the difference between "to" and "as" method name prefixes like
- toList(),
- asList(),
- etc...
When to use which when designing a method?
Software Engineering Stack Exchange is a question and answer site for professionals, academics, and students working within the systems development life cycle. It only takes a minute to sign up.
Sign up to join this communityA toXYZ()
function is expected to do a conversion, and to return a new independent object (though immutability allows for optimization, java.lang.String.toString()
just returns the object).
As an example, in C++ we have std::bitset::to_ulong()
which can easily fail, and a whole plethora of to_string()
, all doing a (more or less) complex conversion and allocating memory.
An asXYZ()
function on the other hand is expected to return a (potentially different) view of the source, doing minimal work.
As an example, in C++ we have std::as_const()
which just returns a constant reference, and the more involved std::forward_as_tuple
which also refers to its arguments by reference.
std::to_string(x)
creates a new string object, but std::as_const(x)
creates a reference (a view) of the existing object.
Jul 5, 2017 at 6:31
Honestly, it may just be naming inconsistency. If look at standard library objects in Smalltalk, for instance, all methods that do "complex conversions" or "return simple representations in another type" are prefixed with as
, as in asString
, asFloat
, asSeconds
, and the standard method for converting anything to anything else, as: aClass
.
In Ruby, the same kinds of methods are prefixed with to_
, as in to_s
, to_a
, to_h
, short for string
, array
and hash
respectively.
Neither standard libraries seem to differentiate between different kinds of conversions, probably because it should be regarded as an implementation detail.
However, in Java we see a lot of mix-up. As you mentioned, there's toString
, asList
, and so on. I believe these are just a naming inconsistency, because if you try to define a different meaning to each prefix, you'll always find a counter-example somewhere else in the standard library.
So in any case, I'd say the important thing is for you and your team to pick one prefix and use it consistently throughout the code. Consistency is the key, so people are not left to wonder, like you had to.
toString
creates a new string fully disconnected from the object (and there's no other way because of immutability). The same holds e.g., for Collection#toArray
, while Arrays#asList
returns a view of the array, which is bidirectionally connected (mutating the array changes the list and vice versa). So it's fairly consistent, though there may be exceptions. Picking one prefix would be wrong. If there was Arrays#toList
, then I'd expect it to create a new list with a new underlying array.
Jul 5, 2017 at 8:35
While there's already an accepted answer, it seems to focus on C++, while the question is tagged with java. In Java, the first example that comes to mind for this kind of thing is Arrays.asList, which returns, essentially, an view of an array, wrapped up in a list. The underlying array and the list are still connected though; changes to the array are reflected in the list, and vice versa. However, the array returned by the list's toArray method is independent from the original array and from the list:
String[] wordArray = {"one", "fine", "day"};
List<String> wordList = Arrays.asList(wordArray);
// changes to the array are visible in the list
System.out.println(wordList); // prints "[one, fine, day]"
wordArray[1] = "horrible";
System.out.println(wordList); // prints "[one, horrible, day]"
// changes to the list are visible in the array
wordList.set(1, "beautiful");
System.out.println(wordArray[1]); // prints "beautiful"
// but changes to the list or array don't affect the
// result from the list's toArray method.
String[] moreWords = wordList.toArray(new String[] {});
wordList.set(0, "the");
wordArray[1] = "best";
for (int i=0; i<3; i++) {
System.out.println(moreWords[i]); // prints "one", "beautiful", and "day"
}
All that said, there's no guarantee that every library developer follows this convention, so you still need to check the documentation to find out whether this is the behavior you'll get from unknown code.
The other place that I've seen as...() methods used frequently is in downcasting types to sub-types. E.g., if you have an enumerated set of subtypes, then you might end up with code like:
/**
* Every Node is either an ANode or a BNode.
*/
interface Node {
/**
* Returns this Node as an ANode.
*
* @return this node
*/
default ANode asANode() {
if (this instanceof ANode) {
return (ANode) this;
}
else {
throw new UnsupportedOperationException();
}
// Or, in Java8 style, perhaps:
// return Optional.of(this)
// .filter(ANode.class::isInstance)
// .map(ANode.class::cast)
// .orElseThrow(UnsupportedOperationException::new);
}
/**
* Returns this Node as a BNode.
*
* @return this node
*/
default BNode asBNode() {
if (this instanceof BNode) {
return (BNode) this;
}
else {
throw new UnsupportedOperationException();
}
}
}
The difference I noticed (just now by thinking about it) is
So we see AsInteger and AsString and we see ToArray and ToStringList.
To implies a conversion, which makes sense (it is a movement, a process). As implies a representation, a way of expressing the original object.
Another way of lookjng at this:
And then there is "prior art" (or legacy) to deal with. Before languages were fully OO from the ground up you would have library functions like StrToInt() and IntToStr(). They performed conversions, they were operations so it made sense to call them SomethingToSomethingelse(). After all, To is more active than As. I am particularly thinking about Delphi here.
When C# was designed with the ambition to go OO all the way, it made sense to have a method on the now integer object that would convert the integer to a string. Although we also have a Convert class, converting to string is so common that it was made a virtual method on object. The designers may have figured that ToString would be more familiar to people from the old paradigm and perhaps this is why we got a virtual method ToString() and not a virtual property AsString.