Почему вспомогательный конструктор не видит импорт в классе?

Код ниже дает ошибку компиляции:

  class Info(val x: String)

  object Info {
    val default = new Info("top")
  }

  case class Data(x: String) {
    import Info.default

    def this() = this(default.x)

  }

Ошибка: (11, 23) не найден: значение по умолчанию     def this() = this (default.x)

Почему символ default не отображается в конструкторе, несмотря на импорт?

Дальнейшие эксперименты показывают, что это не только импорт. Замена строки импорта с помощью def (или даже val) не помогает, по-прежнему сохраняется ошибка:

def default = Info.default

Ответ 1

Скопинг не работает так, как вы ожидали, из-за определения самоподтверждения конструктора (когда вторичные конструкторы ссылаются на первичный конструктор):

Подпись и вызов конструктора self конструктора определение проверяются по типу и оцениваются в области, которая находится в эффект в точке определения охватывающего класса, дополненный любые параметры входящего класса и любые ранние определения прилагаемого шаблона.

Другими словами, область действия для выражения default.x - это область вне Data.

Это сбивает с толку, потому что текстовое выражение похоже на то, что это выражение находится внутри класса.

Это правило связано с лексической областью, а не с порядком оценки.

Вам нужно переместить импорт за пределы определения класса.

Для удовольствия, область видимости слегка нарушена в следующем случае:

object Playroom {
  def default = "playing"
  case class Toy(squeezeMsg: String = default)
  object Toy {
    def default = "srsly"
  }
}

object Test extends App {
  import Playroom._
  println(Toy())
  println(new Toy())
}

который этот головоломка. Вообще говоря, более идиоматично (и надежно) использовать методы apply на компаньоне.

Ответ 2

Это ведет себя по спецификациям. Тело класса является частью так называемого основного конструктора. вспомогательный конструктор (ы) должен начинаться с вызова первичного или другого вспомогательного устройства, определенного ранее. Предполагая, что у вас есть только одно вспомогательное средство, поскольку вызов первичного должно быть первым оператором в вашем вспомогательном устройстве, это означает, что во время этого первого утверждения у вас нет доступа к чему-либо, определенному внутри первичного. После завершения первого вызова вы можете получить доступ к любому члену и импорту, определенному в первичном.

В вашем примере проблема заключается в том, что импорт (import Info.default) не отображается в первом утверждении вашего вспомогательного устройства. Если вы замените его на def this() = this(Info.default.x), он должен работать.

Пример:

Следующие компиляции отлично, так как вызов Constant выполняется после вызова первичного.

class G(i: Int) {
   val Constant = 1
   // No argument auxiliary constructor    
   def this() = {
      this(3) // Call to primary 
      println(Constant * 2)
   }
}

Следующее не компилируется, поскольку вызов Constant выполняется перед вызовом первичного, что означает, что Constant еще не создан.

class G(i: Int) {
   val Constant = 1
   // No argument auxiliary constructor    
   def this() = {
      this(Constant) // This will cause an error!
   }
}

Решение:

Имеются ли какие-либо константы, которые вам нужны в сопутствующем объекте. Сопутствующий объект сначала инициализируется определением, поэтому вы сможете получить доступ к любым его членам внутри конструкторов вашего класса.

object G {
   val Constant = 1
}

class G(i: Int) {
    // No argument auxiliary constructor    
    def this() = {
       this(G.Constant) // This now works!
    }
 }

Ответ 3

В scala любые поля, определенные в классе, недоступны до тех пор, пока не вызывается первичный конструктор. Для исправления обычно используется объект-компаньон с помощью метода apply.