简体   繁体   中英

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?

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} Iterator[T] with Object {def next: T; var i: T; def hasNext: Boolean} 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).

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
}

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.

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