WestCoastProjects
WestCoastProjects

Reputation: 63062

"Constant" values in case class

Consider the "constant" value: DefaultEncoding. Typically we would add it to the companion object:

object Strlen {
  val DefaultEncoding = "ISO-8859-1"
  ..
}

However, we are compelled to avoid the use of a companion object because Strlen is required to be a case class to correspond to code structures/conventions in a significant existing codebase:

case class Strlen(child: Expression, encoding : Expression) extends UnaryExpression with LengthExpression {

  val DefaultEncoding = "ISO-8859-1"    // This will not be isible in the following constructor

  def this(child: Expression) = this(child, new Literal(DefaultEncoding, StringType))

So then would there be any way to achieve compartmentalization of the DefaultEncoding 'constant' within the case class?

Update From suggestion by wingedsubmariner, I tried the following within the case class:

def DefaultEncoding = "ISO-8859-1"

However it does not compile

[info] Compiling 1 Scala source to /shared/spark-master/sql/catalyst/target/scala-2.10/classes...
[error] /shared/spark-master/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringOperations.scala:253: not found: value DefaultEncoding
[error]   def this(child: Expression) = this(child, new Literal(/* StrConstants. */DefaultEncoding, StringType))
[error]       

Upvotes: 2

Views: 6532

Answers (2)

Utaal
Utaal

Reputation: 8534

Writing a custom companion object for a case class won't prevent the compiler from providing the default helper methods for the case class.

trait Expression
trait UnaryExpression extends Expression
trait LengthExpression extends Expression

trait Typ
case object StringType extends Typ

case class Literal(val encoding: String, val typ: Typ) extends Expression

case class StrLen(child: Expression, encoding: Expression) extends UnaryExpression with LengthExpression {
  def this(child: Expression) = this(child, new Literal(StrLen.DefaultEncoding, StringType))
}

object StrLen {
  val DefaultEncoding = "ISO-8859-1"
  def apply(child: Expression): StrLen = apply(child, new Literal(StrLen.DefaultEncoding, StringType))
}

case object ExampleExpression extends Expression

println(StrLen(ExampleExpression))
// --> StrLen(ExampleExpression,Literal(ISO-8859-1,StringType))
println(new StrLen(ExampleExpression))
// --> StrLen(ExampleExpression,Literal(ISO-8859-1,StringType))
def isProduct[T <: Product] {}
isProduct[StrLen]

The default apply is still provided by the compiler (and used in def apply(child: Expression), StrLen still extends Product and toString still does the right thing. The custom constructor def this(child: Expression) is probably unnecessary, as you can use the single-parameter apply instead.

Upvotes: 4

wingedsubmariner
wingedsubmariner

Reputation: 13667

You can use companion objects with case classes. If you declare an object with the same name in the same compilation unit (usually the same file) it will be treated as the case class's companion object and have the usual case class companion object methods, e.g. unapply, added to it.

The other option is to declare DefaultEncoding as a def. Because field access is always done through accessors in Scala this doesn't have any performance penalty.

Upvotes: 3

Related Questions