스칼라에서 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입니다. 나는 scala의 기본값이 val임을 이해합니다. 이:

def age = 30
age = 45

... 기본적으로 val이므로 컴파일 오류가 발생합니다. 왜 위의 첫 번째 줄이 제대로 작동하지 않고 오류가 발생하지 않습니까?

답변:


254

스칼라에는 세 가지 방법으로 정의 할 수 있습니다.

  • def방법을 정의
  • val고정 값을 정의 합니다 (수정할 수 없음)
  • var변수를 정의 합니다 (수정 가능)

코드를 보면 :

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

이것은라는 새로운 방법을 정의합니다 person. 이 메소드 ()는 매개 변수가없는 메소드로 정의되므로이 메소드를 호출하지 않아도 호출 할 수 있습니다 . 빈 빈 메소드의 경우 '()'를 사용하거나 사용하지 않고 호출 할 수 있습니다. 당신이 단순히 쓰는 경우 :

person

그런 다음이 메소드를 호출합니다 (반환 값을 지정하지 않으면 버려집니다). 이 코드 줄에서 :

person.age = 20

결과는 먼저 person메서드를 호출하고 반환 값 (class 인스턴스 Person)에서 age멤버 변수를 변경하는 것 입니다.

그리고 마지막 줄 :

println(person.age)

여기서 person메소드를 다시 호출하여 클래스의 새 인스턴스 Person( age12 로 설정)를 리턴합니다 . 다음과 같습니다 :

println(person().age)

27
혼동을주기 위해 a의 내부 상태를 val변경할 수 있지만 val이 참조하는 객체는 변경할 수 없습니다. A val는 상수가 아닙니다.
— pferrel

5
더 혼란스럽게하기 위해 val (그리고 var도 시도하지 않았지만)을 사용하여 함수를 정의 할 수 있습니다. def를 사용하여 함수 / 메소드를 정의 할 때 def의 본문은 호출 될 때마다 평가됩니다. val을 사용할 때는 정의 지점에서만 평가됩니다. 참조 stackoverflow.com/questions/18887264/...
— melston

1
@melston 네, 그러나 메소드 와 함수 도 정확히 같은 것은 아닙니다 .
— Jesper

3
더 혼란스럽게하기 위해 def는 var를 사용하지 않아도 클래스의 멤버 변수를 정의하는 데 사용할 수도 있습니다.
— Peiti Li

2
@pferrel은 실제로 혼란스럽지 않습니다. Java의 final과 동일합니다. 당신은 표시 할 수 있습니다 List으로 final하지만, 그 내용을 수정할 수 있습니다.
— jFrenetic

100

def , val 및 var 사이의 Scala에 존재하는 구별로 시작합니다 .

  • def- 오른쪽 내용에 대한 불변 레이블 을 정의합니다.이 내용은 느리게 평가되며 이름으로 평가됩니다.

  • val- 값으로 평가되는 즉시 / 즉시 평가 되는 오른쪽 컨텐츠에 대한 불변 레이블 을 정의합니다 .

  • VAR은 - 정의 변경 가능한 변수 , 초기 평가 오른쪽의 내용으로 설정합니다.

예, def

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

예, val

scala> val somethingelse = 2 + 3 * 5 // it's 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')에 대해 변경 불가능한 레이블 이 작성됩니다. 변경 가능한 필드 'age'를 수정해야 할 때마다 이러한 시도는 실패합니다.

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'를 수정할 수 있습니다.

나는 위에서 언급 한 차이점에서 비롯된 모든 것이 스칼라 프로그래머를 염두에 두어야한다는 사실을 여전히 강조합니다.


위의 설명이 정확하다고 생각하지 않습니다. 다른 답변을 참조하십시오.
— Per Mildner

@PerMildner 위의 답변에서 무엇이 잘못되었는지 자세히 설명해 주시겠습니까?
— Syed Souban

원래 불만이 무엇인지 기억이 나지 않습니다. 그러나 답변의 마지막 부분은 personAet al. 떨어져 보인다. 개질 여부 age구성원 작품 아닌지 사용하는지 여부와 상관없이 def personA나 var personB. 차이점은 첫 번째 평가에서 반환 된 인스턴스를 def personA수정 하는 경우 Person입니다 personA. 이 인스턴스 는 수정되었지만 다시 한번 평가할 때 반환되는 것은 아닙니다 personA. 대신, 두 번째로 personA.age당신은 효과적으로하고 new Person("Tim",25).age있습니다.
— Per Mildner

29

와

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

이름이 "Kumar"이고 나이가 12 인 새 Person 인스턴스를 항상 리턴하는 함수 / 게으른 변수를 정의하고 있습니다. 이는 완전히 유효하며 컴파일러가 불평 할 이유가 없습니다. person.age를 호출하면 새로 생성 된이 Person 인스턴스의 나이가 항상 12입니다.

쓸 때

person.age = 45

Person 클래스의 age 속성에 새 값을 할당합니다.이 값은 age가로 선언 된 이후에 유효합니다 var. 컴파일러는 다음 person과 같은 새로운 Person 객체 로 다시 할당하려고 할 때 불평합니다

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

예. 이 점은 쉽게에서 hashCode 메소드 호출에 의해 설명 될 수 페르소나
— Nilanjan 사카를

26

또 다른 관점을 제공하기 위해 Scala에서 "def"는 사용될 때 마다 평가 될 것이지만 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

Kintaro가 이미 말했듯이 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

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) 이 있기 때문에 오류는 없습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.