Kilian Scheltat
Kilian Scheltat

Reputation: 1

Concrete and abstract Vector classes

I went trough scaladoc and found something that I find strange and I cannot explain. There is the

sealed abstract class Vector 

with its companion object Vector. Looking through Vector.scala it looks like that the following is happening

val test: Vector[Int] = Vector(1,2,3)

Here I am calling the apply method of the companion object which is using some factory method to create an object that is a subclass of Vector and has a concrete implementation. So the real type of test is not Vector[Int] but some other subclass.

When I now print the class

print(test.getClass()) 

it returns Vector which is an abstract class so the object can't be of type Vector.

Is there a way to print the actual class of the object? Similar to when I create my own class without an overriding toString scala uses the toString implementation of Any(?) to print out myClass@memorLocation.

Upvotes: 0

Views: 193

Answers (1)

Mario Galic
Mario Galic

Reputation: 48420

Use getClass to get the runtime class of an object. The rest of the answer expands on Luis' comment.


Scala 2.13.2 introduced implementation subclasses for Vector

A second major difference lies in the implementation. Whereas the old Vector uses a single class for collections of all supported sizes, the new one is split into Vector0 to Vector6 for the different dimensions of the main data array. Vector0 is a singleton object for the empty vector, Vector1 (used for collections up to size 32) is essentially the same as ArraySeq, all higher dimensions are finger trees.

/* Contents of Vector.scala in 2.13.2 */

object Vector extends StrictOptimizedSeqFactory[Vector]
sealed abstract class Vector[+A] ...
private sealed abstract class VectorImpl[+A](...) extends Vector[A]
private sealed abstract class BigVector[+A](...) extends VectorImpl[A]
private object Vector0 extends BigVector[Nothing]
private final class Vector1[+A](...) extends VectorImpl[A]
private final class Vector2[+A](...) extends BigVector[A]
private final class Vector3[+A](...) extends BigVector[A]
private final class Vector4[+A](...) extends BigVector[A]
private final class Vector5[+A](...) extends BigVector[A]
private final class Vector6[+A](...) extends BigVector[A]
...

which were not present in Scala 2.13.1

/* Contents of Vector.scala in 2.13.1 */

object Vector extends StrictOptimizedSeqFactory[Vector]
final class Vector[+A] ...
...

Hence the runtime class of Vector(1) in 2.13.2 is Vector1

Welcome to Scala 2.13.2 (OpenJDK 64-Bit Server VM, Java 1.8.0_202).
Type in expressions for evaluation. Or try :help.

scala> Vector(1).getClass.getCanonicalName
val res0: String = scala.collection.immutable.Vector1

whilst the runtime class of Vector(1) in 2.13.1 is Vector

Welcome to Scala 2.13.1 (OpenJDK 64-Bit Server VM, Java 1.8.0_202).
Type in expressions for evaluation. Or try :help.

scala> Vector(1).getClass.getCanonicalName
res0: String = scala.collection.immutable.Vector

Conceptually Scala collections differentiate between concrete collection type and implementation subclasses

/** Defines the prefix of this object's `toString` representation.
 *
 * It is recommended to return the name of the concrete collection type, but
 * not implementation subclasses. For example, for `ListMap` this method should
 * return `"ListMap"`, not `"Map"` (the supertype) or `"Node"` (an implementation
 * subclass).
 *
 * The default implementation returns "Iterable". It is overridden for the basic
 * collection kinds "Seq", "IndexedSeq", "LinearSeq", "Buffer", "Set", "Map",
 * "SortedSet", "SortedMap" and "View".
 *
 *  @return  a string representation which starts the result of `toString`
 *           applied to this $coll. By default the string prefix is the
 *           simple name of the collection class $coll.
 */
protected[this] def className: String = stringPrefix

For example, we refer to Vector1 is an implementation subclass whilst Vector as a concrete collection class even though Vector is technically speaking an abstract class.

We can observe implementation subclasses at play for other collection types as well, for example

scala> Set(1).getClass.getCanonicalName
val res1: String = scala.collection.immutable.Set.Set1

Implementation subclasses are usually a private implementation detail:

the concrete runtime class that you get from Map(...) or toMap is an implementation detail and the vast majority of the time you shouldn't need to worry about it (but when you do, you can check with getClass).

Upvotes: 1

Related Questions