How to distinguish between objects with different abstract type fields?

前端 未结 3 1397
天命终不由人
天命终不由人 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条回答
  • 2021-01-15 22:04

    side question: Because type fields are translated to parameter types in Java ?

    There are no type parameters in bytecode, so type fields could not possibly be translated into them. Type fields only exist during compilation.

    Alternative : We can alternativley create an enumeration and put an additional field in the class A in order to distinguish the objects. But this smells, because it would mean, that we re-implement the type system.

    There's only one kind of type question you can make at run time: what class is this? Scala 2.10 gives a bit more information, but it still boils down to that question.

    If you actually subclass A, you can get that information through reflection (on 2.10). Otherwise, I don't think so -- I've tested on REPL, and there it doesn't work, but things compiled to class files have more information on them.

    Any other question has to be made about values.

    Question : Is there a way to differ between the types of the objects with the help of the type field ? And if not, what will be a good workaround ?

    No, unless Scala 2.10, reflection, and subclasses. Good workaround? Use values. The usual way to handle it is not enumeration, though, but Manifest, which can be generated implicitly.

    Look up questions about how to get around type erasure in Scala.

    0 讨论(0)
  • 2021-01-15 22:15

    You can use the <%< which tells you in compile-time if two types are compatible.

    scala> class A { type T }
    defined class A
    
    scala> implicitly[A { type T=String } <%< A { type T=Int } ]
    <console>:9: error: could not find implicit value for parameter e: <%<[A{type T = String},A{type T = Int}]
                  implicitly[A { type T=String } <%< A { type T=Int } ]
                            ^
    
    scala> implicitly[A { type T=String } <%< A { type T=String } ]
    res1: <%<[A{type T = String},A{type T = String}] = <function1>
    
    scala> implicitly[A { type T=String } <%< A ]
    res3: <%<[A{type T = String},A] = <function1>
    
    0 讨论(0)
  • 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
    
    0 讨论(0)
提交回复
热议问题