How to reproduce case class behaviour with apply/unapply methods?

后端 未结 2 2009
失恋的感觉
失恋的感觉 2021-01-21 19:02

I tried to replace case class with mundane class and companion object and suddenly get type error.

Code that compiles fine (synthetic example):

trait Ele         


        
2条回答
  •  迷失自我
    2021-01-21 19:27

    Firstly other is Elem[C, A], but after you had tried to match it as Chain(head, tail) it actually matched to Chain[C, some inner B, A](head: Elem[C, inner B], tail: Elem[inner B, A]). After that you create Chain[C, inner B <: Any, A](head: Elem[C, inner B], (tail :: this): Elem[inner B, B])

    But result type must be Elem[C, B], or Chain[C, Any, B]. So compiler trying to cast inner B to Any. But beacause inner B is invariant - you must have exactly Any.

    This is actually better rewrite as follows:

    trait Elem[X, Y] {
      def ::[Z, X](other : Elem[Z, X]) : Elem[Z, Y] = other match {
        case Chain(head, tail) => Chain(head, tail :: this)
        case simple => Chain(simple, this)
      }
    }
    
    final class Chain[A, B, C](val head : Elem[A, B], val tail : Elem[B, C]) extends Elem[A, C]
    
    object Chain {
      def unapply[A,B,C](src : Chain[A,B,C]) : Option[(Elem[A,B], Elem[B,C])] =
        Some( (src.head, src.tail) )
      def apply[A,B,C](head : Elem[A,B], tail : Elem[B,C]) : Chain[A,B,C] =
        new Chain(head, tail)
    }
    

    After this error message becoming much more informative and it is obviously how to repair this.

    However I don't know why that works for case classes. Sorry.

    Working example is:

    trait Elem[+X, +Y] {
      def ::[Z, XX >: X](other : Elem[Z, XX]) : Elem[Z, Y] = other match {
        case Chain(head, tail) => Chain(head, tail :: this)
        case simple => Chain(simple, this)
      }
    }
    
    final class Chain[A, B, C](val head : Elem[A, B], val tail : Elem[B, C]) extends Elem[A, C]
    
    object Chain {
      def unapply[A,B,C](src : Chain[A,B,C]) : Option[(Elem[A,B], Elem[B,C])] =
        Some( (src.head, src.tail) )
      def apply[A,B,C](head : Elem[A,B], tail : Elem[B,C]) : Chain[A,B,C] =
        new Chain(head, tail)
    }
    

    EDITED:

    Eventually I found that:

    case class A[T](a: T)
    List(A(1), A("a")).collect { case A(x) => A(x) }
    // res0: List[A[_ >: String with Int]] = List(A(1), A(a))
    
    class B[T](val b: T)
    object B {
      def unapply[T](b: B[T]): Option[T] = Option(b.b)
    }
    List(new B(1), new B("b")).collect { case B(x) => new B(x) }
    // res1: List[B[Any]] = List(B@1ee4afee, B@22eaba0c)
    

    Obvious that it is compiler feature. So I think no way there to reproduce the full case class behavior.

提交回复
热议问题