scala class constructors and abstract types

1.9k views Asked by At

I want to use an abstract type rather than a type parameter.

In my generic classes constructor, I want to have a parameter of the generic type, but the code doesn't compile:

class SomeOtherClass(val s: S){
    type S
}

The scala compiler error is "not found: type S"

If I use a type parameter instead of an abstract type, then it works:

class SomeClass[T](val t: T){
    //...
}

Does scala force me to use a type parameter rather than an abstract type, if I want to have a generic parameter in the constructor?

Is there another way to do this?

5

There are 5 answers

0
Kaito On BEST ANSWER

You're pretty much forced to use generic type parameters in that case. You can work around it by declaring the type outside the class but then you'd need to instantiate the wrapper and then the object and it would get ugly pretty quickly.

trait FooDef {
  type T
  class Foo(val x: T)
}
val ifd = new FooDef { type T = Int }
val ifoo = new ifd.Foo(5)
val sfd = new FooDef { type T = String }
val sfoo = new sfd.Foo("hi")
def intFoos(f: fd.Foo forSome { val fd: FooDef {type T = Int} }) = f.x + 1
2
drexin On

How should the compiler know what type it should use there? Either you have to specify the type directly, which would not make much sense, or use a generic. There is one way to make it work, but I don't think that it would help you.

class SomeClass(s: SomeClass#S) {
  type S
}

But as SomeClass#S is not defined, there is no Instance of it.

0
Luigi Plinge On

When the abstract type isn't specified, your class needs to be abstract. So you don't need the parameter at all. The equivalent with an abstract type would be:

abstract class SomeOtherClass {
  type S
  val s: S 
}

Then at use-site:

val x = new SomeOtherClass {
  type S = String
  val s = "abc"
}

Without the parameter, the abstract class here is equivalent to a trait. You're better off using a trait because it's less restrictive (you can only extend one base class).

0
Kim Stebel On

Maybe you want something like this? This way you can have multiple instances of AbstractFooFactory each producing Foos with a different value for s.

trait AbstractFooFactory {
  type S
  def makeFoo(s:S):Foo
  class Foo(val s:S) {}
}

object StringFooFactory extends AbstractFooFactory {
  override type S = String
  override def makeFoo(s:String) = new Foo(s)
}

val b = StringFooFactory.makeFoo("bar")
val s:String = b.s
0
theStrawMan On

None of the other answers here capture both parts of the very common pattern that is used for this situation, as below. We use either a trait or an abstract class for the SomeOtherClass (as in Luigi's answer) and then the companion object's apply method is the factory to create instances easily. It takes the s val and the type parameter S can be inferred, which simplifies instantiating the object wherever we use it.

trait SomeOtherClass {
  type S
  val something: S 
}

object SomeOtherClass {
  def apply[S0](s:S0) = new SomeOtherClass {
    type S = S0
    val something = s
  }
}

// usage
SomeOtherClass(12)