78

What's the difference between "to" and "as" method name prefixes like

  • toList(),
  • asList(),
  • etc...

When to use which when designing a method?

closed as primarily opinion-based by Thomas Owens Jul 7 '17 at 12:49

Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise. If this question can be reworded to fit the rules in the help center, please edit the question.

120

A 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.

  • 19
    It turns out that there is some existing precedent for this. In general, As[something] will be akin to a cast, whereas To[something] builds a new object (of a different type) from an existing one. In the case of ToList, it's going to be O(n) and almost certainly more expensive than an "As." See stackoverflow.com/questions/17968469/… – Robert Harvey Jul 4 '17 at 18:27
  • 5
    It's part of selecting descriptive names, and even if this specific distinction isn't codified in the coding standard, violating it breaks the Principle of least astonishment. – Deduplicator Jul 4 '17 at 18:27
  • 11
    I would describe it as, instinctively, I would think of "to" as "change to" and "as" as "treat as". The former implies work to change the thing, the latter implies it's just a difference in how you work with it. – Latty Jul 5 '17 at 1:44
  • 5
    If it means something, it should mean this. I'd suggest that it often doesn't mean anything though, so I wouldn't rely on it in random third party code (without seeing something in the docs). It would be a good convention to adopt in a code base though. – Ben Jul 5 '17 at 4:41
  • 4
    This answer is also accurate in C++: for example std::to_string(x) creates a new string object, but std::as_const(x) creates a reference (a view) of the existing object. – Quuxplusone Jul 5 '17 at 6:31
13

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.

  • 1
    I don't believe in: picking one style for a whole team, rather pick one style for similar cases in a module, consistently. – Daniel Hári Jul 5 '17 at 6:54
  • 12
    In Java, 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. – maaartinus Jul 5 '17 at 8:35
  • I think Smalltalk just made a mistake there, they didn't understand the convention. It's a difficult convention to understand (and even notice) because it is so abstract and it's not particularly impactful. Even the very act of asking this question requires some insight. – usr Jul 5 '17 at 14:35
  • 3
    @usr Smalltalk might have been designed before it become a convention – Bergi Jul 5 '17 at 15:45
6

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();
      }
    }
  }
1

The difference I noticed (just now by thinking about it) is

  • To typically converts to a different reference type (a somewhat complex object)
  • As typically returns a simple value type

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:

  • To is an operation, so you would use it for methods.
  • As is a simple representation, so you would use it for properties.

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.

  • 3
    What about toString()? – Deduplicator Jul 4 '17 at 19:57
  • You got me there. I think AsString would have been more appropriate. Ibhave some additionao thoughts, I will update my answer. – Martin Maat Jul 4 '17 at 20:11
  • I don't think this answer really matces the conventions in C#. Eg. ToList() and AsEnumerable() both return complex objects, the difference is ToList() always returns a new object while AsEnumerable() returns a view or adapter over the original object. – JacquesB Jul 6 '17 at 13:32
  • @JaquesB Apparently different ideas have been applied here and there. Delphi has AsInteger and AsString properties on TField objects (which encapsulate database fields). I may have been biased by this. But then again, the question is tagged with Java, not with either C# or Delphi. – Martin Maat Jul 6 '17 at 14:31

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