Наиболее заметное отличие между и неподписанными блоками со знаком находится в приложении ClickOnce. Если Вы не подпишете его, то пользователи получат страшного "Неизвестного Издателя" предупреждение диалогового окна в первый раз, когда они запускают Ваше приложение. Если Вы подписали его с сертификат от доверяемых полномочий , то они видят диалоговое окно, это менее страшно. Насколько я знаю, подписываясь с сертификатом, который Вы генерируете сами, не влияет на "Неизвестного Издателя" предупреждение. Момент SSL от Comodo имеет примеры диалоговых окон.
существуют некоторые более тонкие различия. Необходимо подписать блок, прежде чем он сможет быть установлен в глобальном кэше сборок (GAC), где он может быть совместно использован несколькими приложениями. Подписание является неотъемлемой частью безопасности доступа к коду (CAS), но я не нашел никого, кто мог получить работу CAS. Я вполне уверен, что и GAC и CAS хорошо работают с сертификатами, которые Вы генерируете сами.
Как говорили многие другие, объект, назначенный val
, нельзя заменить, а объект, назначенный var
, можно. Однако внутреннее состояние указанного объекта может быть изменено. Например:
class A(n: Int) {
var value = n
}
class B(n: Int) {
val value = new A(n)
}
object Test {
def main(args: Array[String]) {
val x = new B(5)
x = new B(6) // Doesn't work, because I can't replace the object created on the line above with this new one.
x.value = new A(6) // Doesn't work, because I can't replace the object assigned to B.value for a new one.
x.value.value = 6 // Works, because A.value can receive a new object.
}
}
Итак, даже если мы не можем изменить объект, назначенный x
, мы можем изменить состояние этого объекта. Однако в основе этого лежала var
.
Теперь неизменяемость - это хорошо по многим причинам. Во-первых, если объект не меняет внутреннее состояние, вам не нужно беспокоиться, если какая-то другая часть вашего кода меняет его. Например:
x = new B(0)
f(x)
if (x.value.value == 0)
println("f didn't do anything to x")
else
println("f did something to x")
Это становится особенно важным для многопоточных систем. В многопоточной системе может произойти следующее:
x = new B(1)
f(x)
if (x.value.value == 1) {
print(x.value.value) // Can be different than 1!
}
Если вы используете исключительно val
, и используйте только неизменяемые структуры данных (то есть избегайте массивов, всего в scala.collection.mutable
и т. д.), можете быть уверены, что этого не произойдет. То есть, если нет некоторого кода, возможно, даже фреймворка, выполняющего трюки с отражением - отражение, к сожалению, может изменить «неизменяемые» значения.
Это одна причина, но есть и другая причина. Когда вы используете var
, у вас может возникнуть соблазн повторно использовать одну и ту же var
для нескольких целей. Здесь есть некоторые проблемы:
Проще говоря, использование val
более безопасно и приводит к более читаемому коду.
Тогда мы можем пойти в другом направлении. Если val
так лучше, почему вообще var
? Что ж, некоторые языки действительно пошли по этому пути, но бывают ситуации, в которых изменчивость значительно улучшает производительность.
Например, возьмите неизменяемую Queue
. Когда вы либо ставите в очередь
, либо удаляете из очереди
объекты, вы получаете новый объект Queue
. Как тогда вы будете обрабатывать все элементы в нем?
Я покажу это на примере. Допустим, у вас есть очередь цифр, и вы хотите составить из них число. Например, если у меня есть очередь с 2, 1, 3 в этом порядке, я хочу вернуть число 213. Давайте сначала решим его с помощью mutable.Queue
:
def toNum(q: scala.collection.mutable.Queue[Int]) = {
var num = 0
while (!q.isEmpty) {
num *= 10
num += q.dequeue
}
num
}
Этот код быстрый и простой для понимания. Его главный недостаток заключается в том, что переданная очередь модифицируется на toNum
, поэтому вам необходимо заранее сделать ее копию. Это тот вид управления объектами, от которого неизменность освобождает вас.
Теперь давайте скроем его в неизменяемый.Queue
:
def toNum(q: scala.collection.immutable.Queue[Int]) = {
def recurse(qr: scala.collection.immutable.Queue[Int], num: Int): Int = {
if (qr.isEmpty)
num
else {
val (digit, newQ) = qr.dequeue
recurse(newQ, num * 10 + digit)
}
}
recurse(q, 0)
}
Потому что я не могу повторно использовать какую-то переменную для отслеживания моего num
, как и в предыдущем примере, мне нужно прибегнуть к рекурсии. В данном случае это хвостовая рекурсия, которая имеет довольно хорошую производительность. Но это не всегда так: иногда просто нет хорошего (читаемого, простого) решения хвостовой рекурсии.
Обратите внимание, однако, что я могу переписать этот код, чтобы использовать неизменяемый.Queue
и a var
одновременно! Например:
def toNum(q: scala.collection.immutable.Queue[Int]) = {
var qr = q
var num = 0
while (!qr.isEmpty) {
val (digit, newQ) = qr.dequeue
num *= 10
num += digit
qr = newQ
}
num
}
Этот код все еще работает, не требует рекурсии, и вам не нужно беспокоиться о том, нужно ли вам делать копию очереди перед вызовом toNum
. Естественно, я избегал повторного использования переменных для других целей, и никакой код вне этой функции не видит их, поэтому мне не нужно беспокоиться об изменении их значений от одной строки к другой - кроме случаев, когда я делаю это явно.
Scala предпочел позволить программисту сделать это, если программист сочтет это лучшим решением. Другие языки предпочли усложнить такой код. Цена, которую платит Scala (и любой язык с широко распространенной изменчивостью), заключается в том, что у компилятора не так много возможностей для оптимизации кода, как в противном случае. Ответ Java на это - оптимизация кода на основе профиля времени выполнения. Мы можем продолжать и продолжать говорить о плюсах и минусах каждой стороны.
Лично я Я думаю, что на данный момент Scala обеспечивает правильный баланс. Это далеко не идеально. Я думаю, что и Clojure , и Haskell имеют очень интересные концепции, не принятые в Scala, но у Scala также есть свои сильные стороны. Посмотрим, что нас ждет в будущем.
val
является окончательным, то есть не может быть установлен. Подумайте final
в java.
Разница в том, что var
может быть переназначен, а val
- нет. Изменчивость или отсутствие того, что фактически присвоено, является побочным вопросом:
import collection.immutable
import collection.mutable
var m = immutable.Set("London", "Paris")
m = immutable.Set("New York") //Reassignment - I have change the "value" at m.
Принимая во внимание:
val n = immutable.Set("London", "Paris")
n = immutable.Set("New York") //Will not compile as n is a val.
И, следовательно:
val n = mutable.Set("London", "Paris")
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable.
Если вы создаете структуру данных, и все ее поля имеют значение val
s, то эта структура данных неизменна, так как ее состояние не может измениться.
val
означает неизменяемый, а var
означает изменяемый.
«val означает неизменяемый, а var означает изменяемый».
Перефразируя, " val означает значение, а var означает переменную ".
Различие, которое оказывается чрезвычайно важным в вычислениях (потому что эти две концепции определяют саму суть программирования), и то, что объектно-ориентированный подход удалось почти полностью размыть, потому что в OO единственная аксиома состоит в том, что "