2012-03-27 3 views
1

У меня есть класс, который сериализуется и десериализуется в сеансе, и мне нужно выполнить сопоставление образцов во внутренних классах. У меня возникли проблемы с идентичностью внутренних классов:Удовлетворение идентификатора объекта/соответствия шаблона при сериализации

class Tree(val id: Int) { 
    override def hashCode = id 
    override def equals(that: Any) = that.isInstanceOf[Tree] && 
    that.asInstanceOf[Tree].id == id 

    case object EmptyValue 
} 

val t1 = new Tree(33) 
val t2 = new Tree(33) 

t1 == t2 // ok 
t1.EmptyValue == t2.EmptyValue // wrong -- reports 'false' 

Что такое элегантный способ установить личность для EmptyValue, чтобы не «бежать зависимость от пути», так сказать. У меня есть подобный код, который размыкается при сериализации происходит:

def insert(point: Point, leaf: Leaf): Unit = { 
    val qidx = hyperCube.indexOf(point) 
    child(qidx) match { 
    case EmptyValue => ... 
    ... 
    } 
} 

То есть, хотя компилятор говорит, что мой матч является исчерпывающим, я получаю выполнение MatchError при использовании сериализации (я имею пользовательский код, записывает/читает из массивов байтов). Например, я звоню из дерева, имеющего [email protected], и извлекаю [email protected], и они не совпадают.

EDIT

я провел дополнительные испытания, потому что я действительно хотел бы избежать необходимости перемещать внутренние типы вне, так как они должны иметь параметры типа и, таким образом, разорвать весь шаблон, соответствующий код. Это выглядит так, как если проблема возникает только с этой конкретной case object:

class Tree { 
    sealed trait LeftChild 
    case object EmptyValue extends LeftChild 
    sealed trait LeftNonEmpty extends LeftChild 
    final case class LeftChildBranch() extends LeftNonEmpty 

    def testMatch1(l: LeftChild) = l match { 
    case EmptyValue  => "empty" 
    case LeftChildBranch() => "non-empty" 
    } 

    def testMatch2(l: LeftChild) = l match { 
    case EmptyValue  => "empty" 
    case n: LeftNonEmpty => "non-empty" 
    } 
} 

val t1 = new Tree 
val t2 = new Tree 

t1.testMatch1(t2.LeftChildBranch().asInstanceOf[t1.LeftChild])  // works!!! 
t1.testMatch2(t2.LeftChildBranch().asInstanceOf[t1.LeftChild])  // works!!! 
t1.testMatch1(t2.EmptyValue.asInstanceOf  [t1.EmptyValue.type]) // fails 

ответ

2

Экзистенциальный тип мог бы выразить это.

scala> case class Tree(id: Int) { 
    | case object EmptyValue { 
    |  override def hashCode = 0 
    |  override def equals(that: Any) = that.isInstanceOf[x.EmptyValue.type forSome { val x: Tree }] 
    | } 
    | } 
defined class Tree 

scala> val t1 = Tree(33) 
t1: Tree = Tree(33) 

scala> val t2 = Tree(33) 
t2: Tree = Tree(33) 

scala> t1.EmptyValue == t2.EmptyValue 
res0: Boolean = true 

байт-код для equals:

scala> :javap -v Tree.EmptyValue 

... 
public boolean equals(java.lang.Object); 
    Code: 
    Stack=1, Locals=2, Args_size=2 
    0: aload_1 
    1: instanceof #23; //class Tree$EmptyValue$ 
    4: ireturn 
... 
+0

Это замечательно Юджин. Знаете ли вы, что это определение 'equals' представляет собой какое-то время исполнения (отражение, отличное от' isInstanceOf')? –

+0

Я взял на себя смелость, чтобы отредактировать ваш ответ, показывая полученный байт-код и, таким образом, отвечая на вопрос моего комментария - кажется, это действительно «дешевая» операция :) –

1

Если я понимаю, что вы пытаетесь сделать правильно, вы можете попробовать добавить объект компаньоном для Дерева и определения EmptyValue в нем:

class Tree(val id: Int) { /******/ } 
object Tree { 
    case object EmptyValue 
} 
+0

Проблема заключается в том, Дерево фактически параметрирован в трех типов, таким образом, 'Дерево [S, D, A]'. Я могу использовать только совпадение шаблонов при использовании внутренних типов, потому что в противном случае дерьмовый помощник жалуется на стирание типа. Это довольно сложная структура данных, перемещение внутренних типов обойдется мне в день работы. Я отредактировал вопрос, чтобы показать, что это странно - это только проблема «случайных объектов», как кажется. –