How to distinguish between objects with different abstract type fields?

前端 未结 3 1396
天命终不由人
天命终不由人 2021-01-15 21:58

Assume, we have an abstract class with an abstract type field:

abstract class A {type T}

Now let\'s assume, we have a method, which returns

3条回答
  •  梦毁少年i
    2021-01-15 22:21

    As you guessed, abstract type members are a compile time information that will be erased during type erasure. Here is a work around that uses implicit parameters. The dispatch is static.

    scala> class A {
         |   type T
         | }
    defined class A
    
    scala> implicit def distString(a: A { type T = String }) = "String"
    distString: (a: A{type T = String})java.lang.String
    
    scala> implicit def distInt(a: A { type T = Int }) = "Int"
    distInt: (a: A{type T = Int})java.lang.String
    
    scala> implicit def distOther[O](a: A { type T = O }) = "Other"
    distOther: [O](a: A{type T = O})java.lang.String
    
    scala> def distinguish(a: A)(implicit ev: A { type T = a.T } => String) = ev(a)
    distinguish: (a: A)(implicit ev: A{type T = a.T} => String)String
    
    scala> distinguish(new A { type T = String })
    res2: String = String
    
    scala> distinguish(new A { type T = Int })
    res3: String = Int
    
    scala> distinguish(new A { type T = Float })
    res4: String = Other 
    

    Another way:

    scala> def dist(a: A)(implicit s: a.T =:= String = null, i: a.T =:= Int = null) =
         |   if (s != null) "String" else if (i != null) "Int" else "Other"
    dist: (a: A)(implicit s: =:=[a.T,String], implicit i: =:=[a.T,Int])String
    
    scala> dist(new A { type T = String })
    res5: String = String
    
    scala> dist(new A { type T = Int })
    res6: String = Int
    
    scala> dist(new A { type T = Float })
    res7: String = Other
    

    Edit:

    If the above solutions do not satisfy you, and you want to reify and introspect this type information at runtime after all, you can do that as well, using something called Manifests.

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    abstract class A {
      type T
      def man: Manifest[T]
    }
    
    object A {
      def apply[X](implicit m: Manifest[X]) = new A { type T = X; def man = m }
    }
    
    
    // Exiting paste mode, now interpreting.
    
    defined class A
    defined module A
    
    scala> def disti(a: A): String = a match {
         |   case _ if a.man <:< manifest[String] => "String"
         |   case _ if a.man <:< manifest[Int] => "Int"
         |   case _ => "Other"
         | }
    disti: (a: A)String
    
    scala> disti(A.apply[String])
    res14: String = String
    
    scala> disti(A.apply[Int])
    res15: String = Int
    
    scala> disti(A.apply[Float])
    res16: String = Other
    

提交回复
热议问题