You need to remember that EVERYTHING is an object, there are no basic/literal types at all; in this case, "5" is really just syntactic sugar for creating a "5" String object, 2 is syntactic sugar for creating the "2" Integer (Number?) object, and the " * " operator is syntactic sugar for the " * " method of the "5" String object == ' "5".times(2) ' (which is similar to Perl's ' "5" x 2 ' construct).
The String class supports a " * " method which creates n copies of it's string value, and concat's them together into a new String object, but the Integer class does not.
Many "basic" objects have explicit conversion methods, but will also frequently support implicit conversion where it makes sense.
Why does it overload "*" on the right, but not on the left side of the string? Python has __rmult__, and it works. Please do not try to make language warts look better by spilling a bucket of architectural-astronauty crap.
You need to remember that EVERYTHING is an object, there are no basic/literal types at all; in this case, "5" is really just syntactic sugar for creating a "5" String object, 2 is syntactic sugar for creating the "2" Integer (Number?) object, and the " * " operator is syntactic sugar for the " * " method of the "5" String object == ' "5".times(2) ' (which is similar to Perl's ' "5" x 2 ' construct).
The String class supports a " * " method which creates n copies of it's string value, and concat's them together into a new String object, but the Integer class does not.
Many "basic" objects have explicit conversion methods, but will also frequently support implicit conversion where it makes sense.