Использование def, val и var в scala
class Person(val name:String,var age:Int )
def person = new Person("Kumar",12)
person.age = 20
println(person.age)
Эти строки кодовых выходов 12
, даже если person.age=20
был успешно выполнен. Я обнаружил, что это происходит, потому что я использовал Def в def person = new Person("Kumar",12)
, Если я использую var или val, вывод 20
, Я понимаю, что по умолчанию это значение val в scala. Это:
def age = 30
age = 45
... выдает ошибку компиляции, потому что это значение по умолчанию. Почему первый набор строк выше не работает должным образом, и все же также не ошибается?
6 ответов
Существует три способа определения вещей в Scala:
def
определяет методval
определяет фиксированное значение (которое нельзя изменить)var
определяет переменную (которая может быть изменена)
Глядя на ваш код:
def person = new Person("Kumar",12)
Это определяет новый метод под названием person
, Вы можете вызвать этот метод только без ()
потому что это определяется как метод без параметров. Для метода empty-paren вы можете вызывать его с или без '()'. Если вы просто напишите:
person
тогда вы вызываете этот метод (и если вы не назначите возвращаемое значение, оно просто будет отброшено). В этой строке кода:
person.age = 20
происходит то, что вы сначала называете person
метод и возвращаемое значение (экземпляр класса Person
) вы меняете age
переменная-член.
И последняя строка:
println(person.age)
Здесь вы снова звоните person
метод, который возвращает новый экземпляр класса Person
(с age
установить на 12). Это так же, как это:
println(person().age)
Я бы начал с того, что в Scala существует различие между def, val и var.
def - определяет неизменяемую метку для содержимого правой стороны, которое лениво оценивается - оценивается по имени.
val - определяет неизменяемую метку для правого содержимого, которое охотно / немедленно оценивается - оценивается по значению.
var - определяет изменяемую переменную, изначально установленную на оцененное содержимое правой стороны.
Пример, определение
scala> def something = 2 + 3 * 4
something: Int
scala> something // now it's evaluated, lazily upon usage
res30: Int = 14
Пример, вал
scala> val somethingelse = 2 + 3 * 5 // it's evaluated, eagerly upon definition
somethingelse: Int = 17
Пример, вар
scala> var aVariable = 2 * 3
aVariable: Int = 6
scala> aVariable = 5
aVariable: Int = 5
В соответствии с вышесказанным, метки от def и val не могут быть переназначены, и в случае любой попытки появится ошибка, подобная приведенной ниже:
scala> something = 5 * 6
<console>:8: error: value something_= is not a member of object $iw
something = 5 * 6
^
Когда класс определен как:
scala> class Person(val name: String, var age: Int)
defined class Person
и затем создается с помощью:
scala> def personA = new Person("Tim", 25)
personA: Person
для этого конкретного экземпляра Person создается неизменяемый ярлык (т. е. personA). Всякий раз, когда необходимо изменить изменяемое поле 'age', такая попытка завершается неудачно:
scala> personA.age = 44
personA.age: Int = 25
как и ожидалось, age является частью неизменяемой метки. Правильный способ работы с этим заключается в использовании изменяемой переменной, как в следующем примере:
scala> var personB = new Person("Matt", 36)
personB: Person = Person@59cd11fe
scala> personB.age = 44
personB.age: Int = 44 // value re-assigned, as expected
как ясно, из ссылки на изменяемые переменные (то есть 'personB') можно изменить изменяемое поле класса 'age'.
Я бы по-прежнему подчеркивал тот факт, что все проистекает из вышеуказанной разницы, которая должна быть понятна любому программисту Scala.
С
def person = new Person("Kumar", 12)
вы определяете переменную function/lazy, которая всегда возвращает новый экземпляр Person с именем "Kumar" и возрастом 12. Это полностью допустимо, и у компилятора нет причин жаловаться. Вызов person.age вернет возраст этого вновь созданного экземпляра Person, который всегда равен 12.
При написании
person.age = 45
Вы присваиваете новое значение для свойства age в классе Person, которое является действительным, поскольку возраст объявлен как var
, Компилятор будет жаловаться, если вы попытаетесь переназначить person
с новым объектом Person, таким как
person = new Person("Steve", 13) // Error
Чтобы представить другую перспективу, "def" в Scala означает что-то, что будет оцениваться каждый раз, когда он используется, тогда как val - это то, что оценивается сразу и только один раз. Здесь выражение def person = new Person("Kumar",12)
влечет за собой то, что всякий раз, когда мы используем "человека", мы получим new Person("Kumar",12)
вызов. Поэтому естественно, что два "персонажа" не связаны между собой.
Я так понимаю Scala(возможно, в более "функциональной" манере). Я не уверен, если
def defines a method
val defines a fixed value (which cannot be modified)
var defines a variable (which can be modified)
Это действительно то, что Скала намеревается иметь в виду, хотя. Мне не очень нравится так думать, по крайней мере...
Как уже сказал Кинтаро, person - это метод (из-за def), который всегда возвращает новый экземпляр Person. Как вы узнали, это будет работать, если вы измените метод на var или val:
val person = new Person("Kumar",12)
Другая возможность будет:
def person = new Person("Kumar",12)
val p = person
p.age=20
println(p.age)
Тем не мение, person.age=20
в вашем коде разрешено, так как вы получаете обратно Person
экземпляр из person
метод, и в этом случае вам разрешено изменить значение var
, Проблема в том, что после этой строки у вас больше нет ссылки на этот экземпляр (как каждый вызов person
будет производить новый экземпляр).
В этом нет ничего особенного, у вас будет точно такое же поведение в Java:
class Person{
public int age;
private String name;
public Person(String name; int age) {
this.name = name;
this.age = age;
}
public String name(){ return name; }
}
public Person person() {
return new Person("Kumar", 12);
}
person().age = 20;
System.out.println(person().age); //--> 12
Давайте возьмем это:
class Person(val name:String,var age:Int )
def person =new Person("Kumar",12)
person.age=20
println(person.age)
и переписать его с эквивалентным кодом
class Person(val name:String,var age:Int )
def person =new Person("Kumar",12)
(new Person("Kumar", 12)).age_=(20)
println((new Person("Kumar", 12)).age)
Увидеть, def
это метод. Он будет выполняться каждый раз, когда вызывается, и каждый раз, когда будет возвращаться (а) new Person("Kumar", 12)
, И это не ошибка в "назначении", потому что это на самом деле не назначение, а просто вызов age_=
метод (предоставляется var
).