2

I am implementing an extension of ml.Transformer in Spark; but this question is Scala specific. Here is an example object (part of a Class/Object pair):

abstract class UDFTransformer(func: UserDefinedFunction,
                     inputFieldNames: Seq[String],
                     outputFieldName: String) extends Transformer with MLWritable with Serializable {
  ... definitions here ...
}

object UDFTransformer extends MLReadable[UDFTransformer] {

  // Since there are no parameters associted with the UDF, there is nothing to save!
  class Writer(instance: UDFTransformer) extends MLWriter {
    override protected def saveImpl(path: String): Unit = {}
  }
  abstract protected class Reader extends MLReader[UDFTransformer]

  override def read: MLReader[UDFTransformer] = new Reader

  override def load(path: String): UDFTransformer = super.load(path)

}

The new Reader does not compile because the class is abstract and cannot be instantiated. But; any child class will have to define it; along with its necessary members. I cannot just make read abstract as well, this gives me a warning Only classes can have declared but undefined methods.

The fundamental problem is that each child class of my UDFTransformer is going to wrap a specific UDF. Therefore, the reader needs to be able to generate a specific UDF object; this can't be declared in the superclass. But this 'factory' belongs in the companion object, not in the abstract class itself.

How can I go about building a companion object for an abstract class that can leave the definition of read undefined?

kingledion
  • 2,263
  • 3
  • 25
  • 39

1 Answers1

2

The normal way to do it is by creating an abstract class or trait for the companion objects. Something like

abstract class UDFTransformerCompanion[T <: UDFTransformer] extends MLReadable[T] {

  abstract def read: MLReader[T]

  override def load(path: String): T = super.load(path)

}

class SomeTransformer extends UDFTransformer { ... }

object SomeTransformer extends UDFTransformerCompanion[SomeTransformer] { 
  override def read: MLReader[SomeTransformer] = ...
}

Not sure why you have the load = super.load override, and it doesn't look like you can have a companion object for the UDFTransformer itself, at least not one extending this abstract class.

See GenericTraversableTemplate for a standard library example.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487