Використання def, val і var у масштабі


158
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 in def person = new Person("Kumar",12). Якщо я використовую var або val, вихід буде 20. Я розумію, що за замовчуванням є val in scala. Це:

def age = 30
age = 45

... дає помилку компіляції, оскільки вона за замовчуванням є val. Чому перший набір рядків вище не працює належним чином, а ще й не помиляється?

Відповіді:


254

Існує три способи визначення речей у Scala:

  • defвизначає метод
  • valвизначає фіксоване значення (яке неможливо змінити)
  • varвизначає змінну (яку можна змінити)

Дивлячись на ваш код:

def person = new Person("Kumar",12)

Це визначає новий метод, який називається person. Цей метод можна викликати лише без, ()оскільки він визначений як метод, що не має параметрів. Для методу "порожній-батьків" ви можете викликати його з або без "()". Якщо ви просто пишете:

person

тоді ви викликаєте цей метод (і якщо ви не призначите значення повернення, воно буде просто відкинуто). У цьому рядку коду:

person.age = 20

що трапляється в тому, що ви спочатку викликаєте personметод, а на зворотному значенні (екземпляр класу Person) ви змінюєте ageзмінну члена.

І останній рядок:

println(person.age)

Тут ви знову викликаєте personметод, який повертає новий екземпляр класу Personageвстановленим на 12). Це те саме, що і це:

println(person().age)

27
Щоб переплутати речі, внутрішній стан а- valва можна змінити, але об'єкт, на який посилається val, не може. А val- не константа.
пферрель

5
Для подальшого плутання речей, для визначення функції можна використовувати val (а може бути і var, я ще не пробував). При використанні def для визначення функції / методу тіло def оцінюється кожного разу, коли воно викликається. При використанні val він оцінюється лише в точці визначення. Див stackoverflow.com/questions/18887264 / ...
melston

1
@melston Так, але метод і функція - теж не те саме .
Джеспер

3
щоб ще більше плутати речі, def може також використовуватися для визначення змінних членів класу, не обов'язково використовувати var.
Пейті Лі

2
@pferrel насправді не бентежить. Те саме, що і у фіналі Java. Ви можете позначити Listяк final, але можете змінити його вміст.
jFrenetic

100

Я б почав з розрізнення, яке існує в 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 (тобто «персонами»). Щоразу, коли «вік» змінного поля потрібно змінювати, така спроба не вдається:

scala> personA.age = 44
personA.age: Int = 25

як і очікувалося, "вік" є частиною етикетки, що не змінюється. Правильний спосіб роботи над цим полягає у використанні змінної змінної, як у наступному прикладі:

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.


Я не вважаю, що наведене пояснення є правильним. Дивіться інші відповіді.
Per Mildner

@PerMildner Чи можете ви, будь ласка, детальніше пояснити, що не так у вищевказаній відповіді?
Syed Souban

Я не пам’ятаю, якою була моя первісна скарга. Однак остання частина відповіді, про personAта ін. здається, вимкнений. Незалежно від модифікації ageроботи членів чи ні , не залежить від того, чи використовуєте ви def personAабо var personB. Різниця полягає в тому, що у def personAвипадку, коли ви Personзмінюєте речовину, повернуту з першої оцінки personA. Цей екземпляр буде змінений, але це не те , що повертається , коли ви в черговий раз оцінити personA. Натомість ви вдруге personA.ageзаймаєтесь ефективно new Person("Tim",25).age.
Per

29

З

def person = new Person("Kumar", 12) 

Ви визначаєте функцію / ледачу змінну, яка завжди повертає новий екземпляр Person з іменем "Kumar" та віком 12. Це абсолютно справедливо, і у компілятора немає підстав скаржитися. Виклик person.age поверне вік цього новоствореного екземпляра Особи, який завжди становить 12 років.

При написанні

person.age = 45

Ви присвоюєте нове значення властивості віку в класі Person, яка діє з моменту оголошення віку як var. Компілятор подасть скаргу, якщо ви спробуєте перепризначити personновий об'єкт Person, як-от

person = new Person("Steve", 13)  // Error

Так. Цю точку можна легко продемонструвати, зателефонувавши методу hashCode на personA
Ніланян Саркар

26

Щоб надати іншу перспективу, "def" у Scala означає щось, що буде оцінюватися кожного разу, коли воно використовується, тоді як val - це те, що оцінюється негайно та лише один раз . Тут вираз def person = new Person("Kumar",12)тягне за собою те, що кожного разу, коли ми будемо використовувати "людина", ми отримаємо new Person("Kumar",12)дзвінок. Тому природно, що два "person.age" не пов'язані між собою.

Це те, як я розумію Скалу (можливо, більш "функціональною"). Я не впевнений, чи

def defines a method
val defines a fixed value (which cannot be modified)
var defines a variable (which can be modified)

це дійсно те, що має намір мати на увазі Скала. Мені не дуже подобається так думати ...


20

Як уже говорить Кінтаро, людина - це метод (через 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методу, і в цьому випадку вам дозволяється змінювати значення a 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

8

Візьмемо ось що:

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це метод. Він буде виконуватись кожного разу, коли він викликається, і кожного разу повертається (a) new Person("Kumar", 12). І це не помилка в "призначенні", оскільки це насправді не призначення, а просто виклик age_=методу (надається var).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.