특성 대신 추상 클래스를 사용하면 어떤 이점이 있습니까?


371

특성 대신 추상 클래스를 사용하면 어떤 이점이 있습니까 (성능 제외)? 대부분의 경우 추상 클래스를 특성으로 대체 할 수있는 것 같습니다.

답변:


371

두 가지 차이점을 생각할 수 있습니다

  1. 추상 클래스에는 생성자 매개 변수와 유형 매개 변수가있을 수 있습니다. 특성은 유형 매개 변수 만 가질 수 있습니다. 미래에는 특성조차도 생성자 매개 변수를 가질 수 있다는 논의가있었습니다.
  2. 추상 클래스는 Java와 완전히 호환됩니다. 랩퍼없이 Java 코드에서 호출 할 수 있습니다. 특성은 구현 코드가 포함되지 않은 경우에만 완전히 상호 운용 가능합니다

172
매우 중요한 부록 : 클래스는 여러 특성에서 상속 할 수 있지만 하나의 추상 클래스 만 상속 할 수 있습니다. 나는 이것이 거의 모든 경우에 사용할 것을 고려할 때 개발자가 묻는 첫 번째 질문이라고 생각합니다.
BAR

15
생명의 은인 : "특정 코드는 구현 코드를 포함하지 않는 경우에만 상호 운용이 가능합니다"
Walrus the Cat

2
abstract-객체 (객체의 분기)를 정의하거나 이끄는 집단 행동이 아직 (준비된) 객체로 구성되지 않은 경우. 특성을 유도해야하는 경우, 즉 기능은 개체를 생성 할 때 절대로 발생하지 않습니다. 개체가 격리에서 나와 통신해야 할 때 기능이 진화하거나 필요합니다.
Ramiz Uddin

5
두 번째 차이점은 Java8에는 존재하지 않는다고 생각하십시오.
Duong Nguyen

14
스칼라 2.12에 따라, 특성은 Java 8 인터페이스 ( scala-lang.org/news/2.12.0#traits-compile-to-interfaces)로 컴파일됩니다 .
케빈 메러디스

209

스칼라 프로그래밍에는 "특성 또는 특성이 아닌가?" 라는 섹션이 있습니다. 이 질문을 해결합니다. 첫 번째 에디션은 온라인으로 제공되므로 여기에 모든 내용을 인용해도 무방합니다. (스칼라 프로그래머라면 누구나이 책을 구입해야합니다) :

재사용 가능한 동작 모음을 구현할 때마다 특성 또는 추상 클래스를 사용할지 결정해야합니다. 확실한 규칙은 없지만이 섹션에는 고려해야 할 몇 가지 지침이 있습니다.

동작이 재사용되지 않으면 구체적인 클래스로 만드십시오. 결국 재사용 가능한 동작은 아닙니다.

관련이없는 여러 클래스에서 재사용 할 수 있다면 특성으로 만드십시오. 특성 만 클래스 계층의 다른 부분에 혼합 할 수 있습니다.

Java 코드에서 상속 하려면 추상 클래스를 사용하십시오. 코드가있는 특성은 가까운 Java 아날로그를 갖지 않기 때문에 Java 클래스의 특성에서 상속하는 것이 어색한 경향이 있습니다. 스칼라 클래스에서 상속하는 것은 Java 클래스에서 상속하는 것과 정확히 같습니다. 한 가지 예외로, 추상 멤버 만있는 스칼라 특성은 Java 인터페이스로 직접 변환되므로 Java 코드에서 상속 될 것으로 예상되는 경우에도 해당 특성을 자유롭게 정의해야합니다. Java와 Scala를 함께 사용하는 방법에 대한 자세한 내용은 29 장을 참조하십시오.

컴파일 된 형식으로 배포하려는 경우 외부 그룹에서 상속 된 클래스를 작성하려는 경우 추상 클래스를 사용하는 것이 좋습니다. 문제는 특성이 멤버를 얻거나 잃을 때 특성을 상속하는 클래스가 변경되지 않은 경우에도 다시 컴파일해야한다는 것입니다. 외부 클라이언트가 동작을 상속하는 대신 동작 만 호출하는 경우 특성을 사용하는 것이 좋습니다.

효율성이 매우 중요한 경우 클래스 사용을 고려하십시오. 대부분의 Java 런타임은 클래스 멤버의 가상 메소드 호출을 인터페이스 메소드 호출보다 빠른 조작으로 만듭니다. 특성은 인터페이스로 컴파일되므로 약간의 성능 오버 헤드가 발생할 수 있습니다. 그러나 문제의 특성이 성능 병목을 구성하고 클래스를 사용하면 실제로 문제가 해결된다는 증거가있는 경우에만이 선택을해야합니다.

위 내용을 고려한 후에도 여전히 모르는 경우 에는 특성으로 시작하십시오. 나중에 언제든지 변경할 수 있으며 일반적으로 특성을 사용하면 더 많은 옵션을 열 수 있습니다.

@Mushtaq Ahmed가 언급했듯이, 특성은 매개 변수가 클래스의 기본 생성자로 전달 될 수 없습니다.

또 다른 차이점은의 치료입니다 super.

클래스와 특성의 다른 차이점은 클래스에서는 super호출이 정적으로 바인딩되고 특성에서는 동적으로 바인딩된다는 것입니다. super.toString클래스 를 작성 하면 어떤 메소드 구현이 호출되는지 정확히 알 수 있습니다. 그러나 특성에 동일한 것을 쓰면 특성을 정의 할 때 수퍼 호출을 호출하는 메소드 구현이 정의되지 않습니다.

자세한 내용은 12 장의 나머지 부분 을 참조하십시오.

편집 1 (2013) :

추상 클래스가 특성과 비교하여 동작하는 방식에는 미묘한 차이가 있습니다. 선형화 규칙 중 하나는 클래스의 상속 계층 구조를 유지한다는 점입니다.이 클래스는 나중에 체인에서 추상 클래스를 푸시하는 경향이 있지만 특성을 행복하게 혼합 할 수 있습니다. 특정 상황에서는 실제로 클래스 선형화의 후자 위치에있는 것이 좋습니다 따라서 추상 클래스를 사용할 수 있습니다. Scala의 클래스 선형화 제한 (mixin order)을 참조하십시오 .

편집 2 (2018) :

스칼라 2.12부터 특성의 이진 호환성 동작이 변경되었습니다. 2.12 이전에는 특성을 멤버를 추가하거나 제거하려면 클래스가 변경되지 않은 경우에도 특성을 상속하는 모든 클래스를 다시 컴파일해야했습니다. 이는 특성이 JVM에서 인코딩 된 방식 때문입니다.

Scala 2.12부터 특성 은 Java 인터페이스로 컴파일 되므로 요구 사항이 약간 완화되었습니다. 특성이 다음 중 하나를 수행하는 경우 해당 서브 클래스에는 여전히 재 컴파일이 필요합니다.

  • 필드 정의 ( val또는 var이지만 상수는 괜찮습니다 – final val결과 유형 없음)
  • 부름 super
  • 본문의 이니셜 라이저 문
  • 수업 연장
  • 올바른 초 특징에서 구현을 찾기 위해 선형화에 의존

그러나 특성이 없으면 바이너리 호환성을 손상시키지 않으면 서 특성을 업데이트 할 수 있습니다.


2
If outside clients will only call into the behavior, instead of inheriting from it, then using a trait is fine-누군가 여기서 차이점이 무엇인지 설명 할 수 있습니까? extendsvs with?
0fnt

2
@ 0fnt 그의 구별은 확장과 관련이 없습니다. 그가 말하는 것은 동일한 컴파일 내에서 특성 만 혼합하면 이진 호환성 문제가 적용되지 않는다는 것입니다. 그러나 API가 사용자가 특성 자체를 혼합 할 수 있도록 설계된 경우 이진 호환성에 대해 걱정해야합니다.
존 콜란 두 오니

2
@ 0fnt : 사이에 전혀 의미 차이가 extends하고 with. 순전히 구문 적입니다. 여러 템플릿에서 상속하면 첫 번째는 extend이고 나머지는 모두 얻 with습니다. with쉼표로 생각하십시오 class Foo extends Bar, Baz, Qux.
Jörg W Mittag

77

스칼라 의 Odersky et al 's Programming 은 가치가있는 것이 무엇이든 의심 할 때 특성을 사용할 것을 권장합니다. 필요할 경우 언제든지 나중에 추상 클래스로 변경할 수 있습니다.


20

여러 추상 클래스를 직접 확장 할 수는 없지만 여러 특성을 클래스로 혼합 할 수 있다는 사실 외에도 특성의 슈퍼 호출이 동적으로 바인딩되기 때문에 특성을 스택 할 수 있다고 언급 할 가치가 있습니다 (이전에 클래스 또는 특성이 혼합되어 있음) 현재 하나).

초록 클래스와 특성의 차이점에 대한 토마스의 대답에서 :

trait A{
    def a = 1
}

trait X extends A{
    override def a = {
        println("X")
        super.a
    }
}  


trait Y extends A{
    override def a = {
        println("Y")
        super.a
    }
}

scala> val xy = new AnyRef with X with Y
xy: java.lang.Object with X with Y = $anon$1@6e9b6a
scala> xy.a
Y
X
res0: Int = 1

scala> val yx = new AnyRef with Y with X
yx: java.lang.Object with Y with X = $anon$1@188c838
scala> yx.a
X
Y
res1: Int = 1

9

추상 클래스를 확장 할 때 서브 클래스가 비슷한 종류임을 나타냅니다. 특성을 사용할 때 반드시 그런 것은 아닙니다.


이것이 실제적인 의미가 있습니까, 아니면 코드를 이해하기 쉽게 만드는 것입니까?
Ralf

8

에서 프로그래밍 스칼라 저자는 추상 클래스는 전통적인 객체 지향이 "-는"관계 특성이 조성물의 스칼라 방향 동안 만드는 것을 말한다.


5

추상 클래스는 동작을 포함 할 수 있습니다-생성자 인수 (특성으로는 불가능)로 매개 변수화 할 수 있으며 작업 엔티티를 나타냅니다. 특성은 하나의 기능, 즉 하나의 기능의 인터페이스 인 단일 기능 만 나타냅니다.


8
특성이 행동을 포함 할 수 없다는 것을 암시하지 않기를 바랍니다. 둘 다 구현 코드를 포함 할 수 있습니다.
Mitch Blevins

1
@Mitch Blevins : 물론 아닙니다. 그것들은 코드를 포함 할 수 있지만 trait Enumerable많은 도우미 함수로 정의 할 때 동작 이라고 부르지 않고 하나의 기능과 연결된 기능 만 호출합니다 .
Dario

4
@Dario "행동"과 "기능"이 동의어로 간주되므로 귀하의 답변이 매우 혼란 스럽습니다.
David J.

3
  1. 클래스는 여러 특성에서 상속 할 수 있지만 하나의 추상 클래스 만 상속 할 수 있습니다.
  2. 추상 클래스에는 생성자 매개 변수와 유형 매개 변수가있을 수 있습니다. 특성은 유형 매개 변수 만 가질 수 있습니다. 예를 들어, 특성 t (i : Int) {}; i 매개 변수가 유효하지 않습니다.
  3. 추상 클래스는 Java와 완전히 호환됩니다. 랩퍼없이 Java 코드에서 호출 할 수 있습니다. 특성은 구현 코드가 포함되지 않은 경우에만 상호 운용성이 있습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.