scala class constructors and abstract types
Asked Answered
G

5

10

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?

Greenlee answered 7/8, 2012 at 11:33 Comment(0)
S
2

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
Squirrel answered 7/8, 2012 at 12:3 Comment(0)
C
1

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).

Coldblooded answered 7/8, 2012 at 17:14 Comment(0)
O
0

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.

Objurgate answered 7/8, 2012 at 11:59 Comment(2)
Funny enough this will allow you to create instances new SomeClass(5.asInstanceOf[SomeClass#S]) {type S = Int}. Note that there is no safety, S is still undefined at the cast.Squirrel
So basically to do it right, I need to look at the other two answers?Equinoctial
C
0

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
Carranza answered 7/8, 2012 at 12:2 Comment(0)
M
0

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)
Mychael answered 27/6, 2018 at 1:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.