스칼라에서 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 , valvar 사이의 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

위와 같이 defval의 레이블은 재 할당 할 수 없으며, 시도 할 경우 아래와 같은 오류가 발생합니다.

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 personAvar 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.