스칼라의 사례 클래스와 클래스의 차이점은 무엇입니까?
a case class
와 a 의 차이점을 찾기 위해 Google을 검색했습니다 class
. 모든 사람들은 클래스에서 패턴 일치를 원할 때 유스 케이스 클래스를 언급합니다. 그렇지 않으면 클래스를 사용하고 equals 및 hash 코드 재정의와 같은 추가 특권을 언급하십시오. 그러나 이것이 클래스 대신 케이스 클래스를 사용해야하는 유일한 이유입니까?
스칼라에는이 기능에 대한 몇 가지 중요한 이유가 있다고 생각합니다. 스칼라 사례 클래스에 대한 설명은 무엇입니까?
케이스 클래스는 생성자 인수에만 의존하는 단순하고 변경 불가능한 데이터 보유 오브젝트 로 볼 수 있습니다 .
이 기능 개념을 통해
- 간단한 초기화 구문 사용 (
Node(1, Leaf(2), None))
) - 패턴 일치를 사용하여 분해
- 암시 적으로 정의 된 동등 비교
상속과 함께 case 클래스는 대수 데이터 유형 을 모방하는 데 사용됩니다 .
객체가 내부에서 상태 저장 계산을 수행하거나 다른 종류의 복잡한 동작을 나타내는 경우 일반 클래스 여야합니다.
기술적으로 클래스와 케이스 클래스 사이에는 차이가 없습니다. 컴파일러가 케이스 클래스를 사용할 때 일부 내용을 최적화하더라도 마찬가지입니다. 그러나 사례 클래스는 대수 데이터 유형을 구현하는 특정 패턴에 대한 보일러 플레이트를 제거하는 데 사용됩니다 .
이러한 유형의 매우 간단한 예는 나무입니다. 예를 들어 이진 트리는 다음과 같이 구현할 수 있습니다.
sealed abstract class Tree
case class Node(left: Tree, right: Tree) extends Tree
case class Leaf[A](value: A) extends Tree
case object EmptyLeaf extends Tree
이를 통해 다음을 수행 할 수 있습니다.
// DSL-like assignment:
val treeA = Node(EmptyLeaf, Leaf(5))
val treeB = Node(Node(Leaf(2), Leaf(3)), Leaf(5))
// On Scala 2.8, modification through cloning:
val treeC = treeA.copy(left = treeB.left)
// Pretty printing:
println("Tree A: "+treeA)
println("Tree B: "+treeB)
println("Tree C: "+treeC)
// Comparison:
println("Tree A == Tree B: %s" format (treeA == treeB).toString)
println("Tree B == Tree C: %s" format (treeB == treeC).toString)
// Pattern matching:
treeA match {
case Node(EmptyLeaf, right) => println("Can be reduced to "+right)
case Node(left, EmptyLeaf) => println("Can be reduced to "+left)
case _ => println(treeA+" cannot be reduced")
}
// Pattern matches can be safely done, because the compiler warns about
// non-exaustive matches:
def checkTree(t: Tree) = t match {
case Node(EmptyLeaf, Node(left, right)) =>
// case Node(EmptyLeaf, Leaf(el)) =>
case Node(Node(left, right), EmptyLeaf) =>
case Node(Leaf(el), EmptyLeaf) =>
case Node(Node(l1, r1), Node(l2, r2)) =>
case Node(Leaf(e1), Leaf(e2)) =>
case Node(Node(left, right), Leaf(el)) =>
case Node(Leaf(el), Node(left, right)) =>
// case Node(EmptyLeaf, EmptyLeaf) =>
case Leaf(el) =>
case EmptyLeaf =>
}
트리는 동일한 구문을 사용하여 (패턴 일치를 통해) 구성 및 해체합니다.이 구문도 인쇄 방법 (마이너스 공백)입니다.
또한 유효하고 안정적인 hashCode를 가지고 있기 때문에 해시 맵 또는 세트와 함께 사용할 수도 있습니다.
- 케이스 클래스는 패턴 일치 가능
- 케이스 클래스는 자동으로 해시 코드를 정의하고
- 케이스 클래스는 생성자 인수에 대한 getter 메소드를 자동으로 정의합니다.
(당신은 이미 마지막 것을 제외한 모든 것을 언급했습니다).
이것들은 정규 수업과의 유일한 차이점입니다.
사례 클래스도 인스턴스 Product
이므로이 메소드를 상속 한다고 언급 한 사람은 없습니다 .
def productElement(n: Int): Any
def productArity: Int
def productIterator: Iterator[Any]
여기서, productArity
는 클래스 매개 변수의 수를 productElement(i)
리턴하고 i 번째 매개 변수를 리턴하며 해당 매개 변수를 productIterator
반복 할 수 있도록합니다.
케이스 클래스에 val
생성자 매개 변수 가 있다고 언급 한 사람은 없지만 일반 클래스의 기본값이기도합니다 ( 스칼라 디자인에 불일치라고 생각합니다 ). 다리 오는 " 불변 " 이라고 언급 한 곳을 암시했다 .
var
케이스 클래스에 대해 각 생성자 인수를 앞에 추가하여 기본값을 대체 할 수 있습니다 . 그러나 케이스 클래스를 변경 가능하게하면 메소드 equals
및 hashCode
메소드가 시간 변형됩니다. [1]
sepp2k는 이미 사례 클래스가 자동으로 생성 equals
되고 hashCode
메소드가 있다고 언급했습니다 .
또한 아무도 경우 클래스가 자동으로 동반자를 만드는 것이 언급되지 object
들어있는 클래스와 같은 이름을 가진 apply
및 unapply
방법. 이 apply
방법을 사용하면 앞에 추가하지 않고 인스턴스를 생성 할 수 있습니다 new
. unapply
추출 방법은 다른 언급하는 패턴 매칭을 가능하게한다.
또한 컴파일러의 속도 최적화 match
- case
케이스 클래스 패턴 매칭 [2]가.
[1] 케이스 클래스는 멋지다
[2] 사례 분류 및 추출기, pg 15 .
Scala의 케이스 클래스 구성은 보일러 플레이트를 제거하기위한 편의로 볼 수도 있습니다.
케이스 클래스를 구성 할 때 Scala는 다음을 제공합니다.
- 클래스와 동반자 객체를 만듭니다.
- 컴패니언 객체
apply
는 팩토리 메서드로 사용할 수 있는 메서드를 구현합니다 . 새 키워드를 사용할 필요가없는 구문 설탕 이점을 얻을 수 있습니다.
클래스는 불변이므로 접근자는 클래스의 변수 또는 속성이지만 뮤 테이터는 없습니다 (변수를 변경할 수 없음). 생성자 매개 변수는 공개 읽기 전용 필드로 자동으로 사용 가능합니다. Java bean 구문보다 사용하기가 훨씬 좋습니다.
- 당신은 또한 얻을
hashCode
,equals
그리고toString
기본적으로 방법과equals
방법은 구조적으로 객체를 비교합니다.copy
방법 (일부 필드 방법에 제공된 새로운 값을 갖는) 개체를 복제 할 수 있도록 생성된다.
앞에서 언급했듯이 가장 큰 장점은 사례 클래스에서 패턴 일치를 할 수 있다는 것입니다. 그 이유 unapply
는 케이스 클래스를 해체하여 필드를 추출 할 수있는 메소드를 가져 오기 때문 입니다.
본질적으로 케이스 클래스 (또는 클래스가 인수를 취하지 않는 경우 케이스 객체)를 만들 때 Scala에서 얻는 것은 팩토리 및 추출기 로 목적을 제공하는 단일 객체입니다 .
사람들이 이미 말한 것과는 별도로 class
,case class
1. Case Class
명시 적으로 필요하지는 않지만 new
클래스를 호출해야합니다.new
val classInst = new MyClass(...) // For classes
val classInst = MyClass(..) // For case class
2. 기본 생성자 매개 변수는에 비공개 class
이지만 공개는case class
// For class
class MyClass(x:Int) { }
val classInst = new MyClass(10)
classInst.x // FAILURE : can't access
// For caseClass
case class MyClass(x:Int) { }
val classInst = MyClass(10)
classInst.x // SUCCESS
3. case class
가치를 기준으로 비교
// case Class
class MyClass(x:Int) { }
val classInst = new MyClass(10)
val classInst2 = new MyClass(10)
classInst == classInst2 // FALSE
// For Case Class
case class MyClass(x:Int) { }
val classInst = MyClass(10)
val classInst2 = MyClass(10)
classInst == classInst2 // TRUE
스칼라의 문서 에 따르면 :
사례 클래스는 다음과 같은 일반 클래스입니다.
- 기본적으로 불변
- 패턴 매칭을 통해 분해 가능
- 참조가 아닌 구조적 평등으로 비교
- 인스턴스화하고 작동하는 간결
case 키워드 의 또 다른 기능은 컴파일러가 Java의 익숙한 toString, equals 및 hashCode 메소드를 포함하여 몇 가지 메소드를 자동으로 생성한다는 것입니다.
수업:
scala> class Animal(name:String)
defined class Animal
scala> val an1 = new Animal("Padddington")
an1: Animal = Animal@748860cc
scala> an1.name
<console>:14: error: value name is not a member of Animal
an1.name
^
그러나 동일한 코드를 사용하지만 사례 클래스를 사용하는 경우 :
scala> case class Animal(name:String)
defined class Animal
scala> val an2 = new Animal("Paddington")
an2: Animal = Animal(Paddington)
scala> an2.name
res12: String = Paddington
scala> an2 == Animal("fred")
res14: Boolean = false
scala> an2 == Animal("Paddington")
res15: Boolean = true
개인 수업 :
scala> case class Person(first:String,last:String,age:Int)
defined class Person
scala> val harry = new Person("Harry","Potter",30)
harry: Person = Person(Harry,Potter,30)
scala> harry
res16: Person = Person(Harry,Potter,30)
scala> harry.first = "Saily"
<console>:14: error: reassignment to val
harry.first = "Saily"
^
scala>val saily = harry.copy(first="Saily")
res17: Person = Person(Saily,Potter,30)
scala> harry.copy(age = harry.age+1)
res18: Person = Person(Harry,Potter,31)
패턴 매칭 :
scala> harry match {
| case Person("Harry",_,age) => println(age)
| case _ => println("no match")
| }
30
scala> res17 match {
| case Person("Harry",_,age) => println(age)
| case _ => println("no match")
| }
no match
대상 : 싱글 톤 :
scala> case class Person(first :String,last:String,age:Int)
defined class Person
scala> object Fred extends Person("Fred","Jones",22)
defined object Fred
사례 클래스가 무엇인지 완전히 이해하려면 :
다음과 같은 케이스 클래스 정의를 가정 해 봅시다.
case class Foo(foo:String, bar: Int)
그런 다음 터미널에서 다음을 수행하십시오.
$ scalac -print src/main/scala/Foo.scala
스칼라 2.12.8은 다음을 출력합니다 :
...
case class Foo extends Object with Product with Serializable {
<caseaccessor> <paramaccessor> private[this] val foo: String = _;
<stable> <caseaccessor> <accessor> <paramaccessor> def foo(): String = Foo.this.foo;
<caseaccessor> <paramaccessor> private[this] val bar: Int = _;
<stable> <caseaccessor> <accessor> <paramaccessor> def bar(): Int = Foo.this.bar;
<synthetic> def copy(foo: String, bar: Int): Foo = new Foo(foo, bar);
<synthetic> def copy$default$1(): String = Foo.this.foo();
<synthetic> def copy$default$2(): Int = Foo.this.bar();
override <synthetic> def productPrefix(): String = "Foo";
<synthetic> def productArity(): Int = 2;
<synthetic> def productElement(x$1: Int): Object = {
case <synthetic> val x1: Int = x$1;
(x1: Int) match {
case 0 => Foo.this.foo()
case 1 => scala.Int.box(Foo.this.bar())
case _ => throw new IndexOutOfBoundsException(scala.Int.box(x$1).toString())
}
};
override <synthetic> def productIterator(): Iterator = scala.runtime.ScalaRunTime.typedProductIterator(Foo.this);
<synthetic> def canEqual(x$1: Object): Boolean = x$1.$isInstanceOf[Foo]();
override <synthetic> def hashCode(): Int = {
<synthetic> var acc: Int = -889275714;
acc = scala.runtime.Statics.mix(acc, scala.runtime.Statics.anyHash(Foo.this.foo()));
acc = scala.runtime.Statics.mix(acc, Foo.this.bar());
scala.runtime.Statics.finalizeHash(acc, 2)
};
override <synthetic> def toString(): String = scala.runtime.ScalaRunTime._toString(Foo.this);
override <synthetic> def equals(x$1: Object): Boolean = Foo.this.eq(x$1).||({
case <synthetic> val x1: Object = x$1;
case5(){
if (x1.$isInstanceOf[Foo]())
matchEnd4(true)
else
case6()
};
case6(){
matchEnd4(false)
};
matchEnd4(x: Boolean){
x
}
}.&&({
<synthetic> val Foo$1: Foo = x$1.$asInstanceOf[Foo]();
Foo.this.foo().==(Foo$1.foo()).&&(Foo.this.bar().==(Foo$1.bar())).&&(Foo$1.canEqual(Foo.this))
}));
def <init>(foo: String, bar: Int): Foo = {
Foo.this.foo = foo;
Foo.this.bar = bar;
Foo.super.<init>();
Foo.super./*Product*/$init$();
()
}
};
<synthetic> object Foo extends scala.runtime.AbstractFunction2 with Serializable {
final override <synthetic> def toString(): String = "Foo";
case <synthetic> def apply(foo: String, bar: Int): Foo = new Foo(foo, bar);
case <synthetic> def unapply(x$0: Foo): Option =
if (x$0.==(null))
scala.None
else
new Some(new Tuple2(x$0.foo(), scala.Int.box(x$0.bar())));
<synthetic> private def readResolve(): Object = Foo;
case <synthetic> <bridge> <artifact> def apply(v1: Object, v2: Object): Object = Foo.this.apply(v1.$asInstanceOf[String](), scala.Int.unbox(v2));
def <init>(): Foo.type = {
Foo.super.<init>();
()
}
}
...
보시다시피 스칼라 컴파일러는 일반 클래스 Foo
와 companion-object를 생성 합니다 Foo
.
컴파일 된 클래스를 살펴보고 우리가 얻은 것에 대해 의견을 나눕니다.
Foo
불변 의 클래스 의 내부 상태
val foo: String
val bar: Int
- 게터 :
def foo(): String
def bar(): Int
- 복사 방법 :
def copy(foo: String, bar: Int): Foo
def copy$default$1(): String
def copy$default$2(): Int
scala.Product
특성 구현 :
override def productPrefix(): String
def productArity(): Int
def productElement(x$1: Int): Object
override def productIterator(): Iterator
scala.Equals
케이스 클래스 인스턴스가 동등성을 비교할 수 있도록 특성을 구현 합니다==
.
def canEqual(x$1: Object): Boolean
override def equals(x$1: Object): Boolean
java.lang.Object.hashCode
equals-hashcode 계약을 준수하기위한 재정의 :
override <synthetic> def hashCode(): Int
- 재정의
java.lang.Object.toString
:
override def toString(): String
new
키워드 로 인스턴스화하기위한 생성자 :
def <init>(foo: String, bar: Int): Foo
Object Foo :- 키워드 apply
없는 인스턴스화 방법 new
:
case <synthetic> def apply(foo: String, bar: Int): Foo = new Foo(foo, bar);
unupply
패턴 일치에서 케이스 클래스 Foo를 사용 하기 위한 추출기 메소드 :
case <synthetic> def unapply(x$0: Foo): Option
- 인스턴스를 하나 더 생성하지 않도록 직렬화 해제로부터 객체를 싱글 톤으로 보호하는 방법 :
<synthetic> private def readResolve(): Object = Foo;
- Foo
scala.runtime.AbstractFunction2
는 그러한 트릭을 수행하기 위해 확장 합니다.
scala> case class Foo(foo:String, bar: Int)
defined class Foo
scala> Foo.tupled
res1: ((String, Int)) => Foo = scala.Function2$$Lambda$224/1935637221@9ab310b
tupled
from object는 2 개의 요소 튜플을 적용하여 새 Foo를 만드는 기능을 반환합니다.
따라서 케이스 클래스는 단지 구문 설탕입니다.
케이스 클래스 컴패니언 객체에는 tupled
방어 유형이 있으며, 유형은 다음과 같습니다.
case class Person(name: String, age: Int)
//Person.tupled is def tupled: ((String, Int)) => Person
내가 찾을 수있는 유일한 유스 케이스는 튜플에서 사례 클래스를 생성 해야하는 경우입니다.
val bobAsTuple = ("bob", 14)
val bob = (Person.apply _).tupled(bobAsTuple) //bob: Person = Person(bob,14)
튜플없이 객체를 직접 생성하여 동일한 작업을 수행 할 수 있지만 arity 20 (튜플이 20 인 튜플) 인 튜플 목록으로 표현 된 데이터 집합이 튜플 링을 사용하는 경우가 가장 좋습니다.
경우 클래스는 함께 사용 할 수있는 클래스입니다 match/case
문.
def isIdentityFun(term: Term): Boolean = term match {
case Fun(x, Var(y)) if x == y => true
case _ => false
}
당신은 그것을 참조 case
그 두번째 매개 변수 var에있다 클래스 재미의 인스턴스옵니다. 이것은 매우 훌륭하고 강력한 구문이지만 모든 클래스의 인스턴스에서는 작동하지 않으므로 케이스 클래스에 대한 제한이 있습니다. 이러한 제한 사항을 준수하면 해시 코드와 등호를 자동으로 정의 할 수 있습니다.
"패턴 매칭을 통한 재귀 적 분해 메커니즘"이라는 애매한 문구는 단지 "와 작동한다"를 의미한다 case
. (실제로 뒤 따르는 인스턴스는 다음에 오는 인스턴스 match
와 비교 (일치)합니다.case
. 스칼라는 두 모두 분해해야하며, 구성 요소를 재귀 적으로 분해해야합니다.)
어떤 사례 클래스 가 유용합니까? 그만큼대수 데이터 유형에 대한 Wikipedia 기사는 이 좋은 고전 예, 목록과 나무를 제공합니다. 현대 함수형 언어에서는 대수 데이터 형식 (비교 방법을 포함하여)을 지원해야합니다.
어떤 사례 클래스 가 유용 하지 않습니까? 일부 객체에는 상태가 있습니다.connection.setConnectTimeout(connectTimeout)
는 사례 클래스가 아닙니다.
이제 Scala 둘러보기 : 사례 클래스를 읽을 수 있습니다 .
클래스와 달리 케이스 클래스는 데이터를 보유하는 데 사용됩니다.
케이스 클래스는 데이터 중심 애플리케이션에 유연하므로 케이스 클래스에서 데이터 필드를 정의하고 동반자 오브젝트에서 비즈니스 로직을 정의 할 수 있습니다. 이러한 방식으로 비즈니스 논리에서 데이터를 분리합니다.
copy 메소드를 사용하면 소스에서 필요한 특성 중 일부 또는 전부를 상속하고 원하는대로 변경할 수 있습니다.
나는 모든 답변이 클래스와 사례 클래스에 대한 의미 론적 설명을 제공했다고 생각합니다. 이것은 매우 관련성이 있지만 스칼라의 모든 초보자는 사례 클래스를 만들 때 어떤 일이 발생하는지 알아야합니다. 이 답변 을 작성 하여 간단히 사례 클래스를 설명합니다.
모든 프로그래머는 사전 구축 된 기능을 사용하는 경우 비교적 적은 코드를 작성하므로 가장 최적화 된 코드를 작성할 수는 있지만 큰 책임이 따릅니다. 따라서 미리 작성된 기능을 매우주의해서 사용하십시오.
일부 개발자는 클래스 파일을 분해하여 볼 수있는 추가 20 개의 메소드로 인해 케이스 클래스 작성을 피합니다.
제발 당신이 경우 클래스 내부의 모든 방법을 확인하려면이 링크를 참조하십시오 .
- 케이스 클래스는 apply 및 unapply 메소드로 compagnon 오브젝트를 정의합니다
- 사례 클래스는 직렬화 가능
- 케이스 클래스는 equals와 같은 코드를 정의합니다
- 생성자의 모든 속성은 val (syntactic sugar)입니다.
주요 기능 중 일부 case classes
는 다음과 같습니다.
- 케이스 클래스는 변경할 수 없습니다.
new
키워드 없이 사례 클래스를 인스턴스화 할 수 있습니다 .- 케이스 클래스는 값으로 비교할 수 있습니다
스칼라 문서에서 가져온 스칼라 바이올린의 샘플 스칼라 코드.
https://scalafiddle.io/sf/34XEQyE/0
'development' 카테고리의 다른 글
강사가 작성한 모든 C 파일이 첫 번째 줄에서 단일 #으로 시작하는 이유는 무엇입니까? (0) | 2020.02.19 |
---|---|
ng-if와 ng-show / ng-hide의 차이점은 무엇입니까 (0) | 2020.02.19 |
CSS-부동 자식 DIV 높이를 부모 높이로 확장 (0) | 2020.02.19 |
여러 테이블에서 SQL 쿼리 반환 데이터 (0) | 2020.02.19 |
레일에서 문자열과 텍스트의 차이점은 무엇입니까? (0) | 2020.02.19 |