0

I need to write method that can create an object based on the return type. The return type will be one one of small number of types known at compile time (although I'll happily accept a run time solution).

If it matters, the types are numeric but not primitive types, e.g. a half precision float, and won't all inherit from Number (or similar). (Can I create a base type that delineates a specific set of subtypes?)

I need to something like

object Thing {
   def apply[T](size: Int): Thing[T] = {

     // The call to makeBuffer[T] is inside another generic.
     // I know there are only a limited number types that T can be
     // so I can implement them individually but the compiler does 
     // not know this so it fails to compile
     val buffer = makeBuffer[T](size)

     // more stuff including calling 3rd party generic APIs 
     // that depend on T
   }

   private def [T]makeBuffer(size: Int): Buffer[T] = {
      // What do I put here to build and return the correct Buffer?
   }

   abstract class Buffer[T](size: Int) {
    def doStuff
   }

   // I can implement the small number of concrete classes that I need
   class FloatBuffer(size: Int) extends Buffer[T](size) {
    override def doStuff = // Allocate a buffer with size bytes
   }
}

I don't know how to

  • explain to the compiler that I know what types T will be.
  • return the appropriate implementation

Runtime solutions I've seen, based on TypeTags or using match, need an input argument to carry the type information which I don't have in this case.

1
  • Sorry for the newbie questions but what has happened with this question? There was one answer before that looks like it's been deleted. I didn't fully agree with it but it offered a different solution (that I didn't think of and that might work in other circumstances). I have posted an answer (that works for me) but that has been down voted twice with no explanation! Commented Mar 15, 2019 at 23:54

2 Answers 2

0

First of all, Thing is an object, and so your return type for apply cannot be Thing. Also, Thing does not take generic parameters, so you can't return a Thing[T].

    case class Thing[T](buffer: Thing.Buffer[T])

    object Thing {

        def apply[T](size: Int, clazz: Class[T]): Thing[T] = {

            // The call to makeBuffer[T] is inside another generic.
            // I know there are only a limited number types that T can be
            // so I can implement them individually but the compiler does
            // not know this so it fails to compile
            val result = Thing(makeBuffer(size, clazz))

            // more stuff including calling 3rd party generic APIs
            // that depend on T

            return result
        }

        private def makeBuffer[T](size: Int, clazz: Class[T]): U forSome {type U <: Buffer[T]} = {
            //limited number of classes, so this is probably possible:
            val float = classOf[Float]
            clazz match {
                case `float` => new FloatBuffer(10).asInstanceOf
                //and other types too
            }
        }

        abstract class Buffer[T](size: Int) {
            def doStuff
        }

        // I can implement the small number of concrete classes that I need
        class FloatBuffer(size: Int) extends Buffer[Float](size) {
            override def doStuff = apply(size, classOf[Float])// Allocate a buffer with size bytes
        }
    }

If you have an actual instance of whatever type you want to store in the buffer and all of your limited types share a method, then you can use structural typing. Your example isn't clear, though.

-2

I think it is just asking to much for the compiler to understand what the generic types will be. I've found the following run time solution:

import scala.reflect.runtime.universe._

object StupidTypes {

  private abstract class LoadableBuffer(size: Int) {
    type T
    // actual code omitted for brevity
  }

  private class LoadableFloatBuffer(size: Int) extends LoadableBuffer(size) {
    type T = Float
    // actual code omitted for brevity
  }

  private class LoadableDoubleBuffer(size: Int) extends LoadableBuffer(size) {
    type T = Double
    // actual code omitted for brevity
  }

  def main(args: Array[String]): Unit = {
    makeBuffer[Float](1)
    makeBuffer[Double](1)
    makeBuffer[Int](1)
  }

  private def makeBuffer[T: TypeTag](size: Int): LoadableBuffer = {
    typeOf[T] match {
      case t if t =:= typeOf[Float] => new LoadableFloatBuffer(size)
      case t if t =:= typeOf[Double] => new LoadableDoubleBuffer(size)
      case _ => throw new Exception("no can do")
    }
  }
}
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.