val и объект внутри класса scala? - PullRequest
31 голосов
/ 10 августа 2010

В чем разница между объявлением поля как val, lazy val и object внутри класса scala, как в следующем фрагменте:

class A

class B {
  val a1 = new A      { def foo = 1 }
  object a2 extends A { def foo = 1 }
  lazy val a3 = new A { def foo = 1 }
}

Ответы [ 7 ]

20 голосов
/ 10 августа 2010

В первом случае любой включенный код выполняется, как только создается класс B. Однако в последнем случае, пока вы на самом деле не используете объект, он не будет создан.

Вы можете увидеть разницу здесь:

class A { println("Creating a new A") }
class B {
  val a1 = new A { println("a1"); def foo = 1 }
  object a2 extends A { println("a2"); def foo = 1 }
}

scala> val b = new B
Creating a new A
a1
b: B = B@1176e8a

scala> b.a2.foo
Creating a new A
a2
res0: Int = 1

Есть также скрытые различия в именах созданных .class-файлов и тому подобное; и, конечно, оба имеют разные типы.

19 голосов
/ 09 декабря 2010

Я не уверен, что aioobe признал значение его ответа , но различные типы фактически представляют собой критическую разницу между vals и objects. В частности, val и lazy val имеют структурный тип (например, A{def foo: Int}), в то время как object имеет одноэлементный тип. В результате вызовы метода foo в val s включают отражение, в то время как вызовы метода foo в object не делают:

class A

class B {
  val a1 = new A      { def foo = printStack }
  object a2 extends A { def foo = printStack }
  lazy val a3 = new A { def foo = printStack }

  def printStack() = 
     new Exception().getStackTrace take 3 foreach println
}

scala> val b = new B
b: B = B@5c750

scala> b.a1.foo   // the val
line124$object$$iw$$iw$B.printStack(<console>:12)
line124$object$$iw$$iw$B$$anon$1.foo(<console>:7)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

scala> b.a2.foo   // the object
line124$object$$iw$$iw$B.printStack(<console>:12)
line124$object$$iw$$iw$B$a2$.foo(<console>:8)
line128$object$$iw$$iw$.<init>(<console>:9)

scala> b.a3.foo   // the lazy val
line124$object$$iw$$iw$B.printStack(<console>:12)
line124$object$$iw$$iw$B$$anon$2.foo(<console>:9)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
18 голосов
/ 10 августа 2010

Одно существенное отличие состоит в том, что значения val могут быть переопределены, а объекты - нет.

class C extends B {                           
  override val a1 = new A { def foo = 2 }     
  override object a2 extends A { def foo = 2 }
}

ведет к:

<console>:9: error: overriding object a2 in class B of type object C.this.a2;
object a2 cannot be used here - classes and objects cannot be overridden
override object a2 extends A { def foo = 2 }
3 голосов
/ 13 января 2012

Еще одно существенное отличие состоит в том, что объекты знают свое имя, а вэл - нет.

3 голосов
/ 10 августа 2010

Полагаю, одно отличие состоит в том, что a1 будет одного подтипа A, а a2 будет другого подтипа A, а именно a2.type.

scala> class A
defined class A

scala> val a1 = new A {def foo = 1}
a1: A{def foo: Int} = $anon$1@a9db0e2

scala> object a2 extends A {def foo = 1}
defined module a2

scala> a1
res0: A{def foo: Int} = $anon$1@a9db0e2

scala> a2
res1: a2.type = a2$@5b25d568

scala> 
2 голосов
/ 01 апреля 2014

Первое практическое отличие состоит в том, что ленивые значения и объекты являются ленивыми, тогда как значения стремятся.

Основное отличие между объектами и ленивыми значениями состоит в том, что объект с точки зрения языков считается "singleton, «который с точки зрения jvm обычно рассматривается как статический член.Определение объекта в данном примере не может быть переопределено, как продемонстрировали другие, по той же причине статические члены не могут быть переопределены: без привязки к экземпляру невозможно представить виртуальный поиск функции.

object Foo { object Bar extends A; }

похож на следующий код Java:

class Foo { 
  private static class Bar extends A{}
  public static Bar Bar = new Bar;
}

Если в приведенном выше примере, если подкласс C расширяет Foo, он не сможет переопределить определение Bar.Статический экземпляр Bar в Java будет доступен как Foo.Bar.C.Bar не означает то же самое, что и (новый C) .Bar.Я мог бы быть немного здесь, на самом деле я не пробовал декомпилировать scala-код, это просто пример, чтобы проиллюстрировать общую концепцию объектов как статических членов.,В прошлый раз, когда я проверял, они были реализованы путем сохранения скрытого поля в классе, которое отслеживало, какие ленивые значения были инициализированы.Для сохранения этого поля требуется блокировка, которая может вызвать проблемы с производительностью.

Одним из основных практических различий между отложенным значением val и объектом является обработка ошибки:

Если у меня есть:

class Foo() { throw new Exception("blah!"); }
object Stuff { object Bar extends Foo { val x = "hi" } }
Stuff.Bar
//exception "blah!" thrown.
Stuff.Bar.x
//NoClassDefFoundError: Could not initialize Stuff$Bar$

тогда как если я это сделаю:

object Stuff2 { lazy val Bar = new Foo() { val x = "hi" } }
Stuff2.Bar
// "blah!"
Stuff2.Bar.x
// "blah!"

«NoClassDefFoundError» может быть действительно запутанным, и, поскольку это ошибка, а не исключение, он может нарушить код обработки ошибок, который (соответственно) перехватывает / регистрирует «Исключение», но позволяетошибки для распространения.Я мог бы даже рассмотреть такого рода ошибку в языке Scala, поскольку этот вариант использования фактически указывает на исключительное условие, а не на ошибку JVM.Я видел NoClassDefFoundErrors при доступе к объектам, которые зависели от внешних ресурсов (например, соединения с базой данных или файлы на диске).Только первый доступ регистрирует основную причину, поэтому для правильной отладки такой проблемы обычно требуется перезапуск сервера приложений.

0 голосов
/ 01 ноября 2011

Это не структурный тип: val a = new A {def foo = 1}

Создает уникальный анонимный подкласс;a.foo вызывает foo в этом классе.

x здесь структурный тип: def bar (x: {def bass: Int})

x.bass будет анализировать x (неизвестного типа)) чтобы найти метод с именем «бас».Он будет работать с рыбой или музыкальными инструментами.;)

Разница между ленивым валом и объектом заключается в следующем:

var someA = (new B).a3
someA = (new B).a3 // ok

var anotherA = (new B).a2
anotherA =  = (new B).a2 // compile error
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...