Использование def, val и var в scala

134
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 ответов

213

Существует три способа определения объектов в Scala:

  • def определяет метод
  • val определяет фиксированное значение (которое не может быть изменено)
  • var определяет переменную (которая может быть изменена)

Глядя на ваш код:

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

Этот параметр определяет новый метод person. Вы можете вызвать этот метод только без (), потому что он определяется как метод без параметров. Для метода empty-paren вы можете вызвать его с помощью или без '()'. Если вы просто пишете:

person

то вы вызываете этот метод (и если вы не назначаете возвращаемое значение, оно просто будет отброшено). В этой строке кода:

person.age = 20

происходит то, что вы сначала вызываете метод person, а в возвращаемом значении (экземпляр класса person) вы изменяете переменную-член age.

И последняя строка:

println(person.age)

Здесь вы снова вызываете метод person, который возвращает новый экземпляр класса personage установлен на 12). Это то же самое, что и:

println(person().age)
  • 23
    Чтобы запутать вещи, внутреннее состояние val может быть изменено, но объект, на который ссылается val, не может. val не является константой.
  • 4
    Чтобы еще больше сбить с толку, для определения функции можно использовать val (и, возможно, var, я не пробовал). При использовании def для определения функции / метода тело def вычисляется при каждом вызове. При использовании val он оценивается только в точке определения. См. Stackoverflow.com/questions/18887264/…
Показать ещё 4 комментария
66

Я бы начал с различия, существующего в Scala между def, val и var.

  • def - определяет неизменяемую метку для содержимого правой стороны, которая лениво оценивается - оценивает по имени.

  • val - определяет неизменяемую метку для содержимого правой стороны, которая нетерпеливо/сразу оценивается - оценивается по значению.

  • var - определяет изменяемую переменную, первоначально установленную для оцениваемого правого содержимого.

Пример, def

scala> def something = 2 + 3 * 4 
something: Int
scala> something  // now it evaluated, lazily upon usage
res30: Int = 14

Пример, val

scala> val somethingelse = 2 + 3 * 5 // it evaluated, eagerly upon definition
somethingelse: Int = 17

Пример, var

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') создается неизменяемая метка. Всякий раз, когда изменяемый поле "возраст" необходимо изменить, такая попытка не выполняется:

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" ) можно изменить изменяемое классом поле "возраст" .

Я бы все же подчеркнул тот факт, что все исходит из вышеупомянутой разницы, которая должна быть понятна любому программисту Scala.

  • 0
    Я не думаю, что приведенное выше объяснение является правильным. Смотрите другие ответы.
30

С

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
24

Чтобы обеспечить другую перспективу, "def" в Scala означает что-то, что будет оцениваться каждый раз, когда оно будет использоваться, а val - это то, что оценивается немедленно и только один раз. Здесь выражение def person = new Person("Kumar",12) подразумевает, что всякий раз, когда мы используем "person", мы получаем вызов new Person("Kumar",12). Поэтому естественно, что два "person.age" не связаны между собой.

Так я понимаю Scala (возможно, более функциональным). Я не уверен, что

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

действительно то, что означает Scala. Мне не очень нравится думать так, как минимум...

18

Как уже говорит Кинтаро, человек - это метод (из-за 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
  • 0
    Этот ответ - самое ясное объяснение.
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).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню