Каков тип вложенного объекта в Scala? - PullRequest
8 голосов
/ 10 февраля 2012

Я пытаюсь выяснить, как создать метод, который принимает вложенный объект в качестве аргумента. Для вложенного класса я могу сделать следующее:

scala> class Outer { 
 | class Inner
 | def method(i:Outer#Inner) = { "inner class" }
 | }
defined class Outer

Однако, если я попробую что-то подобное с объектом, я получу ошибку:

scala> class Outer { 
 | object Inner
 | def method(i:Outer#Inner) = { "inner object" }
 | }
<console>:11: error: type Inner is not a member of Outer
   def method(i:Outer#Inner) = { "inner object" }

Каким должен быть тип аргумента метода для достижения этой цели? Также я хочу сослаться на тип Inner объекта, который не обобщает аргумент, чтобы сказать Any.

Ответы [ 3 ]

13 голосов
/ 10 февраля 2012

Inner - это объект, поэтому он не является типом и не может использоваться как тип. тип Inner равен Inner.type. Это означает, что в вашем примере. К сожалению, каждый экземпляр Outer будет иметь свой собственный внутренний объект, и тип Outer#Inner.type не может быть использован, поскольку он нестабилен. Обходной путь должен использовать: this.Inner.type.

 def method(i:this.Inner.type) = { "inner object" }

Но это означает, что вы можете передать в качестве параметра только Внутренний объект экземпляра, для которого вы вызываете method.

7 голосов
/ 10 февраля 2012

Простой пример, чтобы проиллюстрировать, что здесь происходит (в REPL):

object A
def foo(a : A) = "Does not compile"
def bar(a : A.type) = "Does compile!"
bar(A) // returns "Does compile!"

Как говорит Николас, Inner - это не тип, поэтому его нельзя использовать как таковой.

Пытаясь понять вашу мотивацию, я придумал что-то вроде этого:

class Outer(i : Int) {
  object Inner {
    def getI : Int = i
  }
  def foo(x : Inner.type) = x.getI
}

Это немного бессмысленно, так как мы будем просто ссылаться на Внутренний - в конце концов, есть только один из них:

class Outer(i : Int) {
  object Inner {
    def getI : Int = i
  }
  def foo = Inner.getI
}

Я предполагаю, что вы хотите принять Иннер из любого экземпляра Внешнего. Мы можем проверить тип такой вещи:

val o = new Outer(1)
:type o.Inner
o.Inner.type

Так что мы можем ожидать, что сможем сделать что-то вроде этого:

class Outer(i : Int) {
  object Inner {
    def getI : Int = i
  }
  def foo(x : Outer#Inner.type) = x.getI
}

Это, однако, не компилируется. Я не уверен почему. Введите псевдонимы на помощь!

class Outer(i : Int) {
  type Inner = Inner.type
  object Inner {
    def getI : Int = i
  }
  def foo(x : Outer#Inner) = x.getI
}

val a = new Outer(1)
val b = new Outer(2)
a.foo(b.Inner) //returns 2

Я предполагаю, что это просто ограничение синтаксического анализатора, что он не может прочитать что-то вроде A#B.type. Вы можете отправить запрос об ошибке.

1 голос
/ 24 мая 2016

Каждый Scala object имеет свой собственный тип, и существует ровно одно значение этого типа - сам объект. Поэтому тип Scala object не более полезен, чем тип Unit.

Например, скажем, у вас есть object A и объявите метод или функцию, которая принимает параметр A.type:

def foo(arg: A.type) = {}

Поскольку всегда будет существовать ровно одно значение типа A.type, мы не теряем общности, вместо этого ссылаясь на него непосредственно внутри метода foo.

Если вы захотите сделать это, более вероятно, что то, что вы действительно хотите абстрагироваться, - это черта или базовый класс объекта:

trait Useful { def usefulness: Int }
object A extends Useful { override val usefulness = 42 }
class Foo {
  def foo(arg: Useful) = arg.usefulness
}
...