Marcin Wisnicki
Marcin Wisnicki

Reputation: 4701

Using type parameter in anonymous class in Scala

I'm trying to declare attribute of parametrized type inside anonymous class. This works in Java, in Scala (2.9) however I get compile error:

Parameter type in structural refinement may not refer to an abstract type defined outside that refinement

This is the code:

object DemoFail extends App {
  def it[T <: AnyRef](x: T) = new Iterator[T] {
    var i = x // here is the error
    def next = i
    def hasNext = true
  }
  for (i ← it(int2Integer(4))) println(i)
}

I can get it to work by "erasing" types manually:

object DemoOK extends App {
  def it[T <: AnyRef](x: T) = new Iterator[T] {
    var i: AnyRef = x
    def next = i.asInstanceOf[T]
    def hasNext = true
  }
  for (i ← it(int2Integer(4))) println(i)
}

So the question is: why can't the compiler do it for me ?

Upvotes: 3

Views: 1157

Answers (3)

Alex Archambault
Alex Archambault

Reputation: 985

A quick fix would be to avoid the structural return type:

object DemoFail extends App {
  // The same with an explicit (non structural) return type
  //                       vvvvvvvvvvvvv
  def it[T <: AnyRef](x: T): Iterator[T] = 
    new Iterator[T] {
      var i = x // no more error
      def next = i
      def hasNext = true
    }
  for (i ← it(int2Integer(4))) println(i)
}

Indeed, method it on object DemoFail does not have an explicit return type. Hence this return type is inferred by the compiler.

Here, as you are overriding existing members and adding a new one to Iterator[T], the inferred return type of the method it is a structural type of the form Iterator[T] with Object {def next: T; var i : T; def hasNext: Boolean} (as an IDE like IntelliJ can suggest).

Thus you are defining a method whose return type is a structural type that uses an abstract type of this same method. This is what bothers scalac (structural type with same method's abstract type).

Upvotes: 1

Kris Nuttycombe
Kris Nuttycombe

Reputation: 4580

By adding a public variable to your iterator, you create a structural type that is a subtype of Iterator. It'll work if you change i to a private variable.

Upvotes: 1

Rex Kerr
Rex Kerr

Reputation: 167921

I am, unfortunately, not sure why this doesn't work. But here is an alternate workaround that avoids casts:

def it[T <: AnyRef](x: T) = {
  class Forever extends Iterator[T] {
    var i = x
    def next = i
    def hasNext = true
  }
  new Forever
}

Upvotes: 1

Related Questions