2010-11-09 2 views
11

Я задаю несколько иной вопрос, чем this one. Предположим, у меня есть фрагмент кода:Плюсы и минусы выбора def over val

def foo(i : Int) : List[String] = { 
    val s = i.toString + "!" //using val 
    s :: Nil 
} 

Это функционально эквивалентно следующему:

def foo(i : Int) : List[String] = { 
    def s = i.toString + "!" //using def 
    s :: Nil 
} 

Почему я выбираю один над другим? Очевидно, я предполагаю, что второй имеет незначительные недостатки в:

  • создавая больше байткод (внутренняя def поднимается к методу в классе)
  • производительность выполнения накладные расходы вызова метода через доступ к значению
  • нестрогие оценка означает, что я мог бы легко получить доступ к s дважды (т.е. unnecesasarily повторить расчет)

Единственное преимущество я могу думать:

  • нестрогие оценка s означает, что она вызывается только тогда, когда оно используется (но тогда я мог бы просто использовать lazy val)

Какие мысли народов здесь? Есть ли существенная диспропорция для меня, делая все внутреннее val s def s?

+3

Но если используется только один раз, когда 'lazy val' будет медленнее, чем' def'. – Debilski

+0

Это правда, но я ожидаю, что чрезмерное использование 'def's вызовет массивный * класс bloat –

+2

Но опять же, это в равной степени верно и для' lazy val's. Таким образом, преимущество все еще сохраняется. – Debilski

ответ

0

Для локального объявления, подобного этому (без аргументов, оцененных ровно один раз и без кода, оцененного между точкой объявления и точкой оценки), нет семантической разницы. Я не удивлюсь, если версия «val» скомпилирована для более простого и более эффективного кода, чем версия «def», но вам нужно будет проверить байт-код и, возможно, профиль, чтобы быть уверенным.

+0

«def» получит «поднятый» метод во «внешнем» классе, тогда как val будет только стандартным значением –

0

В вашем примере я бы использовал val. Я думаю, что выбор Вэл/четкости является более значимым при объявлении членов класса:

class A { def a0 = "a"; def a1 = "a" } 

class B extends A { 
    var c = 0 
    override def a0 = { c += 1; "a" + c } 
    override val a1 = "b" 
} 

В базовом классе с использованием def позволяет классу суб заместить, возможно, опр не возвращает константу. Или он может переопределить с val. Это дает большую гибкость, чем val.

Редактировать: еще один пример использования def over val - это когда абстрактный класс имеет «val», для которого значение должно быть предоставлено подклассом.

abstract class C { def f: SomeObject } 
new C { val f = new SomeObject(...) } 
+0

Опять же - в моем примере 'def' ** не может быть ** отменено, так как это объявленный в методе –

3

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

Внутренне компилятор будет отмечать его как STABLE, что эквивалентно окончательному в Java. Это должны позволить JVM, чтобы сделать все виды оптимизаций - я просто не знаю, что они :)

+0

Но если я использую его только один раз, и это чисто внутри метода, это не похоже на то, что в любом случае есть что-то вроде одновременного доступа, верно?Я знаю, в чем разница между двумя вещами, мне интересно, какие другие аргументы могут быть решены в защиту использования 'def' –

+0

a val - это просто def (помечено как стабильная) плюс поле поддержки. Если я знаю, что я всегда буду предоставлять эту поддержку в каком-либо подклассе (например, родительский абстрактный), то я предпочитаю def. Нет особо сильной логики для этого, просто ощущение, что val - это конкретная конкретная реализация этого метода. –

+0

Аналогично ... с внутренним val. Я знаю, что я не буду подклассифицировать его, поэтому я бы использовал только def или lazy val, где это дорогостоящее вычисление, которое может не понадобиться. –

2

я вижу преимущество в том, что вы менее связных к месту при использовании def чем при использовании val.

Это не техническое преимущество, но в некоторых случаях оно позволяет лучше структурировать.

Итак, глупый пример (измените этот ответ, если у вас есть лучше один), это не представляется возможным с val:

def foo(i : Int) : List[String] = { 
    def ret = s :: Nil 
    def s = i.toString + "!" 
    ret 
} 

Там могут быть случаи, когда это важно или просто удобно.

(Так что, в принципе, вы можете достичь того же с lazy val, но, если только называется не более одного раза, это, вероятно, будет быстрее, чем lazy val.)

6

1)

Один ответа я Жду» Следует заметить, что фрейм стека для описываемого метода может быть меньше. Каждый объявляемый вами займет слот в стеке JVM, однако всякий раз, когда вы используете полученное значение def, оно будет потребляться в первом выражении, в котором вы его используете. Даже если def ссылается на что-то из среды, компилятор пройдет , HotSpot должен оптимизировать обе эти вещи, или, как утверждают некоторые люди. См:

http://www.ibm.com/developerworks/library/j-jtp12214/

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

Но, возьмите это с (большим) зерном соли - на самом деле я не сделал обширных тестов для резервного копирования этого утверждения.

2)

Кроме того, чтобы расширить допустимый ответ Кевина, стабильный val обеспечивает также означает, что вы можете использовать его с path dependent types - то, что вы не можете сделать с def, так как компилятор не проверьте его чистоту.

3)

По другой причине вы можете захотеть использовать def см родственный вопрос задают не так давно:

Functional processing of Scala streams without OutOfMemory errors

По существу, используя def с для получения Streams гарантирует, что не существует дополнительных ссылок на эти объекты, что важно для GC. Поскольку Stream s ленивы в любом случае, накладные расходы на их создание, вероятно, незначительны, даже если у вас есть несколько def.

Смежные вопросы