A method in Scala that takes no arguments and uses

2019-09-04 16:14发布

问题:

I'm trying to write a method in Scala that will take no arguments and will use a generic type to perform some logic in the method that will return output solely based on the generic type (similar to asInstanceOf[T] or isInstanceOf[T]).

It should be something like this:

val myObj = new MyClass
myObj.instanceOf[MyClass]
// returns true

This is what I thought may work.

class MyClass {
  def instanceOf[Class[_]]: Bool = {
    // ???
  }
}

How do I implement this?

Thank you.

回答1:

You could grab the type that was passed in by using ClassTags and use that.

A contrived example of what is asked in the title would be:

class Foo(name: String) {
  def nameMatches[T: ClassTag]() =
    classTag[T].runtimeClass.getName == name
}

new Foo("java.lang.String").nameMatches[String] //> res1: Boolean = true
new Foo("boolean").nameMatches[Boolean]         //> res2: Boolean = true


回答2:

This will work for the example you gave.

import scala.reflect.runtime.universe._

class MyClass { 
  def instanceOf[T: TypeTag] = typeOf[this.type] <:< typeOf[T] 
}

Note that when you extend MyClass you will need to override instanceOf to get correct behaviour. Else you will get this:

scala> class MySubClass extends MyClass
defined class MySubClass

scala> (new MySubClass).instanceOf[MyClass]  // works
res3: Boolean = true

scala> (new MySubClass).instanceOf[Any]  // works
res4: Boolean = true

scala> (new MySubClass).instanceOf[MySubClass]  // doesn't work
res5: Boolean = false


回答3:

Another example of a generic method would be the standard library's implicitly, which you can trivially implement yourself:

def implicitly[T](implicit ev: T) = ev

so you can do things like look up type class instances:

trait Show[T] { def shows(t: T): String }
case class Person(name: String, age: Int)
implicit val personShow = new Show[Person] { def show(p: Person) = s"${p.name} @ ${p.age} years" }

implicitly[Show[Person]]

this is useful in two cases:

  1. you define a non-trivial type class instance using def (i.e. you generically "generate" type class instances from instances of other type classes) and you're not sure you got it right:

    implicit def terriblyNonTrivialTCInstance[T, U, V, W, Ñ](implicit ev1: Foo, ev2: Bar, ev3: Baz, ...): FooTC[T, U] = ...
    
    implicitly[FooTC[MyType1, MyType2]]  // compile error if the above definition is badly constructed
    
  2. your method takes an implicit evidence but instead of using an implicit arg list, you prefer to use a type bound, and later you'd like to grab an instance of the type class using implicitly:

    def myMethod[T: FooTC[T, Int]](t: T) = {
      val ev = implicitly[FooTC[T, Int]]
      ev.doSmth(t)
    }
    

    instead of:

    def myMehtod[T](t: T)(implicit ev: FooTC[T, Int]) = {
      ev.doSmth(t)
    }
    

P.S. in the stdlib, implicitly is implemented like this:

@inline def implicitly[T](implicit e: T) = e