如何为案例类创建显式伴随对象,其行为与替换编译器提供的隐式伴随对象相同?

时间:2014-08-19 20:28:21

标签: scala case-class companion-object

我有一个案例类定义如下:

case class StreetSecondary(designator: String, value: Option[String])

然后我定义了一个显式伴随对象:

object StreetSecondary {
  //empty for now
}

定义显式伴随对象StreetSecondary的行为导致编译器生成"隐含伴随对象"迷路;即替换为无法访问编译器生成的版本。例如,tupled方法可通过此隐式伴随对象在案例类StreetSecondary上使用。但是,一旦我定义了明确的伴随对象,tupled方法就会丢失"。

那么,我需要定义/添加/更改上面的StreetSecondary显式伴随对象,以重新获得所有丢失的功能,并替换编译器提供的隐式伴随对象?我想要的不仅仅是恢复的tupled方法。我想要恢复所有功能(例如,包括提取器/ unapply)。

感谢您提供的任何指导/指导。


更新1

我已经做了足够的搜索来发现几件事:

A)显式伴随对象必须在其case类之前定义(至少在Eclipse Scala-IDE WorkSheet中就是这种情况,并且代码在IntelliJ IDE的WorkSheet中不起作用,无论如何其中第一个)。

B)强制tupled有一个技术诀窍(感谢drstevens):(StreetSecondary.apply _).tupled虽然这解决了特定的tupled方法问题,但它仍然没有准确或完整地描述scala编译器在隐式伴随对象中提供的内容。

C)最后,可以定义显式伴随对象以扩展与主构造函数的参数的签名匹配的函数,并返回案例类的实例。它看起来像这样:

object StreetSecondary extends ((String, Option[String]) => StreetSecondary) {
  //empty for now
}

同样,我仍无法准确或完整地描述scala编译器在隐式伴随对象中提供的内容。

3 个答案:

答案 0 :(得分:4)

为案例类定义显式伴随对象(从Scala 2.11开始),要完全替换丢失的隐式伴随对象中的编译器提供的功能,显式伴随对象的基本模板有两个要求:

要求:
 1.必须扩展一个函数定义,它由一个元组组成(完全匹配case类构造函数参数的类型和顺序),返回case类的类型
 2.必须覆盖toString函数以提供对象类名称(与关联的案例类相同)

这是"空"的原始示例代码。显式伴侣对象:

object StreetSecondary {
  //empty for now
}

以下是实施上述要求后的示例代码:

object StreetSecondary extends ((String, Option[String]) => StreetSecondary) {
    //replace the toString implementation coming from the inherited class (FunctionN)
    override def toString =
      getClass.getName.split("""\$""").reverse.dropWhile(x => {val char = x.take(1).head; !((char == '_') || char.isLetter)}).head
}

为满足上述要求1,在对象名称之后和第一个大括号之前插入extends ((String, Option[String]) => StreetSecondary)

为了满足上面的要求2,override def toString = getClass.getName.split("""\$""").reverse.dropWhile(x => {val char = x.take(1).head; !((char == '_') || char.isLetter)}).head被插入到对象的主体中(显式实现仍然有问题)

深深感谢@drstevens发布javap输出以帮助我获得信心,上述两个步骤是恢复丢失功能所需的全部内容。

答案 1 :(得分:2)

Scala 2.11.0

当您在配套文件中未提供其他功能时,看起来scalac正在预定义tupled功能

case class BB(a: Int, b: Int)
object BB { }

case class AA(a: Int, b: Int)

object CC { }
case class CC(a: Int, b: Int)

结果如下

public class AA implements scala.Product,scala.Serializable {
  public static scala.Option<scala.Tuple2<java.lang.Object, java.lang.Object>> unapply(AA);
  public static AA apply(int, int);
  public static scala.Function1<scala.Tuple2<java.lang.Object, java.lang.Object>, AA> tupled();
  public static scala.Function1<java.lang.Object, scala.Function1<java.lang.Object, AA>> curried();
  public int a();
  public int b();
  public AA copy(int, int);
  public int copy$default$1();
  public int copy$default$2();
  public java.lang.String productPrefix();
  public int productArity();
  public java.lang.Object productElement(int);
  public scala.collection.Iterator<java.lang.Object> productIterator();
  public boolean canEqual(java.lang.Object);
  public int hashCode();
  public java.lang.String toString();
  public boolean equals(java.lang.Object);
  public AA(int, int);
}

public final class AA$ extends scala.runtime.AbstractFunction2<java.lang.Object, java.lang.Object, AA> implements scala.Serializable {
  public static final AA$ MODULE$;
  public static {};
  public final java.lang.String toString();
  public AA apply(int, int);
  public scala.Option<scala.Tuple2<java.lang.Object, java.lang.Object>> unapply(AA);
  public java.lang.Object apply(java.lang.Object, java.lang.Object);
}

public class BB implements scala.Product,scala.Serializable {
  public static scala.Option<scala.Tuple2<java.lang.Object, java.lang.Object>> unapply(BB);
  public static BB apply(int, int);
  public int a();
  public int b();
  public BB copy(int, int);
  public int copy$default$1();
  public int copy$default$2();
  public java.lang.String productPrefix();
  public int productArity();
  public java.lang.Object productElement(int);
  public scala.collection.Iterator<java.lang.Object> productIterator();
  public boolean canEqual(java.lang.Object);
  public int hashCode();
  public java.lang.String toString();
  public boolean equals(java.lang.Object);
  public BB(int, int);
}

public final class BB$ implements scala.Serializable {
  public static final BB$ MODULE$;
  public static {};
  public BB apply(int, int);
  public scala.Option<scala.Tuple2<java.lang.Object, java.lang.Object>> unapply(BB);
}

public class CC implements scala.Product,scala.Serializable {
  public static scala.Option<scala.Tuple2<java.lang.Object, java.lang.Object>> unapply(CC);
  public static CC apply(int, int);
  public int a();
  public int b();
  public CC copy(int, int);
  public int copy$default$1();
  public int copy$default$2();
  public java.lang.String productPrefix();
  public int productArity();
  public java.lang.Object productElement(int);
  public scala.collection.Iterator<java.lang.Object> productIterator();
  public boolean canEqual(java.lang.Object);
  public int hashCode();
  public java.lang.String toString();
  public boolean equals(java.lang.Object);
  public CC(int, int);
}

public final class CC$ implements scala.Serializable {
  public static final CC$ MODULE$;
  public static {};
  public CC apply(int, int);
  public scala.Option<scala.Tuple2<java.lang.Object, java.lang.Object>> unapply(CC);
}

答案 2 :(得分:1)

为什么你认为你正在失去它们?

case class Person(name: String, age: Int)
object Person {
  def apply(): Person = new Person("Bob", 33)
}

val alice = Person("Alice", 20)
val bob   = Person()

Person.unapply(alice)    //Option[(String, Int)] = Some((Alice,20))
Person.unapply(Person()) //Option[(String, Int)] = Some((Bob,33))

好像我还有提取器。

在你的情况下,你仍然得到了所有:

scala> StreetSecondary.unapply _
res10: StreetSecondary => Option[(String, Option[String])] = <function1>