简体   繁体   中英

type-parametrized object in Scala

Is there a way to write object with generic parameters, like that:

object Aaa[T] {
   def f(a: T) = a
}

Or, in other words, to have singleton on instance-level, but not on the type level.

I know that I could do the same with:

object Aaa {
   def f[T](a: T) = a
}

But what if I have several methods to restrict with single polymorphic type:

object Aaa[T] {
   def f1(a: T) = a
   def f2(b: T) = b
}

//somewhere in the code:
val a = Aaa[Int]
import a._

f1(5)
f2(6)
someFunction(a)

PS All I want is singleton factory with type-parameter as input (and as a key). Usually it's implemented with Map[TypeTag, Object] (which requires thread-safety, btw) - looking for more nice solution here. For example with "parametrize method" approach I can't:

trait T1[T] {
   def f1(a: T): T
   def f2(b: T): T
}

object Aaa extends T1 { //won't compile, have to use class
   //some heavy initialization and data here
   (1 to 100500000).map(List.empty[T]) 
   def f1[T](a: T) = a
   def f2[T](b: T) = b
}

It might be some method that creates a big structure and requires generic type to be specified.

And of course this object may be passed to another function (or value), so single polymorphic type restriction really works.

Yes, that's possible using .asInstanceOf :

trait AaaImpl[T] {
   this: Aaa.type =>
   def f(a: T) = a
}

object Aaa extends AaaImpl[Nothing] { // lower bound of T should be here
   def apply[T] = this.asInstanceOf[AaaImpl[T]]
}

// Exiting paste mode, now interpreting.

defined trait AaaImpl
defined module Aaa

scala> Aaa[Int].f(5)
res7: Int = 5

scala> Aaa[Double].f(5.0)
res8: Double = 5.0

It's safe to cast here as long as your object doesn't do any other typecasts. asInstanceOf just copying your type (using AaaImpl[Nothing] as prototype) but with new type parameter (like case class 's copy does in values world).

PS Trait's methods will be also available inside Aaa itself, but Nothing will be used for T

PS2 You may also implement some other traits to pass this object to some external library:

//External
trait SomeAbstractType[T] {
   def f(a: T): T
}

def ff[T](x: SomeAbstractType[T]) = x

//Internal
trait AaaImpl[T] { def f(a: T) = a }

object Aaa extends AaaImpl[Nothing] with SomeAbstractType[Nothing] { // lower bound of T should be here
   def apply[A] = this.asInstanceOf[AaaImpl[A] with SomeAbstractType[A]]
}

// Exiting paste mode, now interpreting.

defined trait SomeAbstractType
ff: [T](x: SomeAbstractType[T])SomeAbstractType[T]
defined trait AaaImpl
defined module Aaa

scala> ff(Aaa[Int])
res11: SomeAbstractType[Int] = Aaa$@6e18a830

scala> ff(Aaa[Double])
res12: SomeAbstractType[Double] = Aaa$@6e18a830 //same instance

scala> ff(Aaa[Int]).f(5) //different behaviour
res15: Int = 5

scala> ff(Aaa[Double]).f(5.0)
res16: Double = 5.0

Update1. Examples of something cooler than identity:

scala> trait AaaImpl[T] {
   def list(a: T) = List(a)
   def empty = List[T]()
   def square(a:T)(implicit n:Numeric[T]) = n.times(a, a)
}
defined trait AaaImpl

scala> object Aaa extends AaaImpl[Nothing]{ // lower bound of T should be here
   def apply[A] = this.asInstanceOf[AaaImpl[A]]
}
defined module Aaa

scala> Aaa[Int].list(5)
res21: List[Int] = List(5)

scala> Aaa[Int].empty
res22: List[Int] = List()

scala> Aaa[Int].square(5)
res23: Int = 25

scala> Aaa[List[Int]].square(5)
<console>:11: error: type mismatch;
 found   : Int(5)
 required: List[Int]
              Aaa[List[Int]].square(5)
                                    ^

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM