Java의 인터페이스와 Haskell의 유형 클래스 : 차이점과 유사점?


112

Haskell을 배우는 동안 Haskell에서 비롯된 훌륭한 발명품 인 클래스 클래스를 발견했습니다 .

그러나 클래스 유형의 Wikipedia 페이지에서 :

프로그래머는 클래스에 속하는 모든 유형에 대해 존재해야하는 함수 또는 상수 이름 세트를 해당 유형과 함께 지정하여 유형 클래스를 정의합니다.

나에게 Java의 인터페이스 에 다소 가까운 것 같습니다 ( Wikipedia의 Interface (Java) 페이지 인용 ).

Java 프로그래밍 언어의 인터페이스는 클래스가 구현해야하는 인터페이스 (일반적인 의미에서)를 지정하는 데 사용되는 추상 유형입니다.

이 두 가지는 다소 비슷해 보입니다. 유형 클래스는 유형의 동작을 제한하는 반면 인터페이스는 클래스의 동작을 제한합니다.

Haskell의 유형 클래스와 Java의 인터페이스 간의 차이점과 유사점이 무엇인지 궁금합니다. 아니면 근본적으로 다른 것일까 요?

편집 : 나는 심지어 haskell.org가 그들이 비슷하다는 것을 인정한다는 것을 알았습니다 . 그들이 그렇게 비슷하다면 (또는 그들이 있습니까?), 왜 타입 클래스가 그러한 과장으로 취급됩니까?

추가 편집 : 와우, 너무 많은 훌륭한 답변이 있습니다! 커뮤니티가 어느 것이 가장 좋은지 결정하도록해야 할 것 같습니다. 그러나 대답을 읽는 동안 그들 모두는 "인터페이스가 제네릭에 대처할 수 없거나 대처해야하는 동안 typeclass가 할 수있는 많은 일이있다"고만 말하는 것처럼 보입니다 . 나는 도울 수 없지만 궁금해 할 수 있습니다. 유형 클래스가 할 수없는 동안 인터페이스가 할 수있는 것이 있습니까? 또한 Wikipedia에서 typeclass가 원래 1989 년 논문 * "임시 다형성을 덜 임시로 만드는 방법"에서 발명되었다고 주장하는 반면 Haskell은 여전히 ​​요람에 있으며 Java 프로젝트는 1991 년에 시작되어 1995 년에 처음 출시되었습니다. 그래서 아마도 typeclass가 인터페이스와 유사한 대신에, 그 반대의 경우 인터페이스가 typeclass의 영향을 받았습니까?이를 뒷받침하거나 반박하는 문서 / 문서가 있습니까? 모든 답변에 감사드립니다. 그들은 모두 매우 깨달았습니다!

모든 입력에 감사드립니다!


3
아니요, 인터페이스가 유형 클래스가 할 수없는 작업을 할 수있는 것은 실제로 없습니다. 인터페이스는 일반적으로 Haskell에서 찾을 수없는 내장 기능이있는 언어로 표시된다는 주요 경고가 있습니다. Java에 유형 클래스가 추가 되었으면 해당 기능도 사용할 수 있습니다.
CA McCann

8
여러 질문이있는 경우 여러 질문을해야하며 모든 질문을 하나의 질문으로 묶으려고하지 마십시오. 어쨌든, 당신의 마지막 질문에 대답하자면, 자바의 주요 영향은 Objective-C (그리고 종종 거짓으로보고되는 C ++가 아닙니다 )이며, 그 주요 영향은 스몰 토크와 C입니다. 자바의 인터페이스 는 Objective-C 프로토콜 의 적응입니다 . OO에서 프로토콜 개념의 공식화 . 이는 차례로 네트워킹, 특히 ARPANet 의 프로토콜 개념에 기반 합니다. 이 모든 일은 당신이 인용 한 논문보다 오래 전에 일어났습니다. ...
Jörg W Mittag 2011-08-05

1
... Java에 대한 Haskell의 영향은 훨씬 나중에 나타 났으며, 결국 Haskell의 디자이너 중 한 명인 Phil Wadler가 공동 설계 한 Generics로 제한됩니다.
Jörg W Mittag 2011-08-05

5
이것은 Java의 원래 디자이너 중 한 명인 Patrick Naughton이 작성한 Usenet 기사입니다. Java는 C ++가 아니라 Objective-C에 의해 강하게 영향을 받았습니다 . 불행히도 너무 오래되어 원래 게시물이 Google 아카이브에 나타나지 않습니다.
Jörg W Mittag

8
이 질문의 정확한 중복으로 마감 된 또 다른 질문이 있지만 훨씬 더 자세한 답변이 있습니다. stackoverflow.com/questions/8122109/…
Ben

답변:


50

인터페이스는 SomeInterface t모든 값이 유형을 갖는 유형 클래스와 비슷하다고 말할 수 있습니다 t -> whatever(여기서는 whatever포함하지 않음 t). 이는 Java 및 유사한 언어의 상속 관계의 종류로 인해 호출되는 메서드가 호출되는 객체 유형에 따라 달라지며 다른 것은 없기 때문입니다.

add :: t -> t -> t, 인터페이스가 하나 이상의 매개 변수에 대해 다형성 인 인터페이스와 같은 것을 만드는 것은 정말 어렵다는 것을 의미합니다 . 왜냐하면 인터페이스가 메소드의 인수 유형과 반환 유형이 메소드의 유형과 동일한 유형임을 지정할 방법이 없기 때문입니다. 호출 된 객체 (예 : "self"유형). 제네릭으로, 객체 자체와 같은 형태가 될 것으로 예상된다 일반적인 매개 변수 인터페이스를함으로써 가짜 이에 대한 방법이 좀있다, 방법처럼 Comparable<T>그것을 않습니다, 당신이 사용할 것으로 예상되는 곳 Foo implements Comparable<Foo>이므로 것을 compareTo(T otherobject)의 종류 유형이t -> t -> Ordering . 하지만 그래도 프로그래머는이 규칙을 따라야하며, 사람들이이 인터페이스를 사용하는 함수를 만들고자 할 때 골칫거리가되고 재귀 적 제네릭 유형 매개 변수가 있어야합니다.

또한 empty :: t여기에서 함수를 호출하지 않기 때문에 같은 것이 없기 때문에 메서드가 아닙니다.


1
스칼라 트레이 트 (기본적으로 인터페이스)는 this.type을 허용하므로 "self type"의 매개 변수를 반환하거나 수락 할 수 있습니다. Scala는 이와 관련이없는 "self types"btw라고 부르는 완전히 별개의 기능을 가지고 있습니다. 이것은 개념적 차이가 아니라 구현 차이 일뿐입니다.
클레이

45

인터페이스와 유형 클래스의 유사한 점은 관련 작업 집합의 이름을 지정하고 설명한다는 것입니다. 작업 자체는 이름, 입력 및 출력을 통해 설명됩니다. 마찬가지로 이러한 작업의 구현이 다를 수있는 많은 구현이있을 수 있습니다.

이를 통해 몇 가지 주목할만한 차이점이 있습니다.

  • 인터페이스 메서드는 항상 개체 인스턴스와 연결됩니다. 즉, 메서드가 호출되는 객체 인 암시 적 'this'매개 변수가 항상 있습니다. 유형 클래스 함수에 대한 모든 입력은 명시 적입니다.
  • 인터페이스 구현은 인터페이스를 구현하는 클래스의 일부로 정의되어야합니다. 반대로, 유형 클래스 '인스턴스'는 다른 모듈에서도 관련 유형과 완전히 분리되어 정의 될 수 있습니다.

일반적으로 타입 클래스가 인터페이스보다 강력하고 유연하다고 말하는 것이 공정하다고 생각합니다. 문자열을 구현 유형의 값 또는 인스턴스로 변환하기위한 인터페이스를 어떻게 정의 하시겠습니까? 확실히 불가능하지는 않지만 결과는 직관적이거나 우아하지 않습니다. 컴파일 된 라이브러리에서 유형에 대한 인터페이스를 구현할 수 있기를 바란 적이 있습니까? 둘 다 유형 클래스로 쉽게 수행 할 수 있습니다.


1
타입 클래스를 어떻게 확장 하시겠습니까? 인터페이스가 인터페이스를 확장하는 방법처럼 타입 클래스가 다른 타입 클래스를 확장 할 수 있습니까?
CMCDragonkai

10
인터페이스에서 Java 8의 기본 구현을 고려 하여이 답변을 업데이트하는 것이 좋습니다.
Monica 복원

1
@CMCDragonkai 예, 예를 들어 "class (Foo a) => Bar a where ..."라고 말하여 Bar 유형 클래스가 Foo 유형 클래스를 확장하도록 지정할 수 있습니다. Java와 마찬가지로 Haskell은 여기에 다중 상속이 있습니다.
Monica 복원

이 경우 유형 클래스가 Clojure의 프로토콜과 동일하지 않지만 유형 안전성이 있습니까?
nawfal

24

유형 클래스는 기본적으로 오버로드 된 함수 의 기술 용어 인 "임시 다형성"을 표현하는 구조화 된 방법으로 생성되었습니다 . 유형 클래스 정의는 다음과 같습니다.

class Foobar a where
    foo :: a -> a -> Bool
    bar :: String -> a

이것이 의미하는 바는 foo클래스에 속하는 유형의 일부 인수에 함수 를 적용 할 때 해당 유형 Foobarfoo특정한 구현을 찾아서 사용한다는 것입니다. 이것은 더 유연하고 일반화 된 것을 제외하고는 C ++ / C #과 같은 언어에서 연산자 오버로딩이있는 상황과 매우 유사합니다.

인터페이스는 OO 언어에서 비슷한 목적을 제공하지만 기본 개념은 약간 다릅니다. OO 언어는 Haskell이 가지고 있지 않은 유형 계층 구조의 기본 제공 개념과 함께 제공됩니다. 이는 인터페이스가 하위 유형 지정 (예 : 적절한 인스턴스에서 메서드 호출, 인터페이스를 구현하는 하위 유형의 상위 유형 수행)에 의한 오버로딩을 모두 포함 할 수 있기 때문에 어떤면에서 문제를 복잡하게 만듭니다. 플랫 타입 기반 디스패치 (인터페이스를 구현하는 두 클래스가 인터페이스를 구현하는 공통 수퍼 클래스가 없을 수 있기 때문에)에 의해. 서브 타이핑에 의해 도입 된 엄청난 추가 복잡성을 감안할 때, 유형 클래스를 비 OO 언어에서 오버로드 된 함수의 개선 된 버전으로 생각하는 것이 더 유용하다고 제안합니다.

또한 주목할 가치가있는 것은 타입 클래스는 훨씬 더 유연한 디스패치 수단을 가지고 있다는 것입니다. 인터페이스는 일반적으로 그것을 구현하는 단일 클래스에만 적용되는 반면 타입 클래스는 클래스 함수의 시그니처 어디에나 나타날 수 있는 타입에 대해 정의 됩니다. OO 인터페이스에서 이와 동등한 것은 인터페이스가 해당 클래스의 객체를 다른 클래스에 전달하는 방법을 정의하고, 어떤 반환 유형을 기반으로 구현을 선택하는 정적 메서드 및 생성자를 정의하도록 허용하는 것입니다. , 호출 컨텍스트에서 필요한 정의하고, 인터페이스를 구현하는 클래스와 동일한 유형의 인수 및 실제로 전혀 번역되지 않는 다양한 기타 항목을 가져옵니다.

요컨대 : 그들은 비슷한 목적을 제공하지만 작동 방식이 다소 다르며 유형 클래스는 모두 훨씬 더 표현력이 뛰어나고 경우에 따라 상속 계층 구조의 일부가 아닌 고정 유형에서 작업하기 때문에 사용하기가 더 간단합니다.


Haskell의 유형 계층 구조를 이해하는 데 어려움을 겪고있었습니다. Omega와 같은 유형 시스템 유형에 대해 어떻게 생각하십니까? 유형 계층 구조를 시뮬레이션 할 수 있습니까?
CMCDragonkai

@CMCDragonkai : 오메가에 대해 잘 모르겠습니다.
CA McCann

16

위의 답변을 읽었습니다. 조금 더 명확하게 대답 할 수 있다고 생각합니다.

Haskell "유형 클래스"와 Java / C # "인터페이스"또는 Scala "특성"은 기본적으로 유사합니다. 그들 사이에는 개념적 차이가 없지만 구현 차이가 있습니다.

  • Haskell 유형 클래스는 데이터 유형 정의와 분리 된 "인스턴스"로 구현됩니다. C # / Java / Scala에서 인터페이스 / 특성은 클래스 정의에서 구현되어야합니다.
  • Haskell 유형 클래스를 사용하면이 유형 또는 자체 유형을 반환 할 수 있습니다. 스칼라 특성도 마찬가지입니다 (this.type). Scala의 "자체 유형"은 완전히 관련이없는 기능입니다. Java / C #에서는이 동작을 근사화하기 위해 제네릭을 사용하는 복잡한 해결 방법이 필요합니다.
  • Haskell 유형 클래스를 사용하면 입력 "this"유형 매개 변수없이 함수 (상수 포함)를 정의 할 수 있습니다. Java / C # 인터페이스 및 Scala 특성에는 모든 함수에 "this"입력 매개 변수가 필요합니다.
  • Haskell 유형 클래스를 사용하면 함수에 대한 기본 구현을 정의 할 수 있습니다. Scala 특성과 Java 8+ 인터페이스도 마찬가지입니다. C #은 확장 메서드를 사용하여 이와 유사하게 만들 수 있습니다.

2
이 답변 포인트에 몇 가지 문헌을 추가하기 위해 오늘 On (Haskell) Type Classes 및 (C #) Interfaces를 읽었 습니다. 이는 Java 대신 C # 인터페이스와 비교되지만 개념적 측면에 대한 이해를 제공해야합니다. 언어 경계를 넘는 인터페이스.
daniel.kahlenberg 2011 년

기본 구현과 같은 몇 가지 근사치는 추상 클래스를 사용하여 C #에서보다 효율적으로 수행되는 것 같습니다.
Arwin

12

에서 프로그램의 마스터 마음 , 하스켈에서 자바 인터페이스 및 유형 클래스 사이의 유사성을 설명 할 필 Wadler 입력 클래스의 발명자와 하스켈에 대한 인터뷰가있다 :

다음과 같은 Java 메소드 :

   public static <T extends Comparable<T>> T min (T x, T y) 
   {
      if (x.compare(y) < 0)
            return x; 
      else
            return y; 
   }

Haskell 방법과 매우 유사합니다.

   min :: Ord a => a -> a -> a
   min x y  = if x < y then x else y

따라서 유형 클래스는 인터페이스와 관련이 있지만 실제 대응은 위와 같은 유형으로 매개 변수화 된 정적 메소드입니다.


에 따라 때문에, 대답해야 필 Wadler 의 홈페이지, 그가 나중에 언어 자체에 포함 된 자바의 제네릭 확장을 설계 동시에, 하스켈의 원칙 디자이너이었다.
왕 지아 하우


8

인터페이스가 할 수없는 여러 문제를 유형 클래스로 해결할 수있는 방법에 대한 예제가 제공되는 소프트웨어 확장 및 유형 클래스와의 통합을 읽어보십시오 .

논문에 나열된 예는 다음과 같습니다.

  • 표현 문제,
  • 프레임 워크 통합 문제,
  • 독립적 인 확장 성 문제,
  • 지배적 인 분해, 산란 및 엉킴의 폭정.

3
링크가 위에 있습니다. 대신 이것을 시도하십시오 .
Justin Leitgeb

1
이제 그 사람도 죽었습니다. 보십시오 이 한
RichardW

7

괜찮아 보인다면 "과장"수준으로 말할 수 없습니다. 그러나 예 유형 클래스는 여러면에서 유사합니다. 내가 생각할 수있는 한 가지 차이점은 당신이 유형 클래스의 일부에 대한 동작을 제공 할 수 하스켈 있다는 것입니다 작업 :

class  Eq a  where
  (==), (/=) :: a -> a -> Bool
  x /= y     = not (x == y)
  x == y     = not (x /= y)

이것은 두 개의 연산, equal (==)및 not-equal 이 있음을 보여줍니다 (/=).Eq 유형 클래스의 . 그러나 같지 않음 연산은 같음 (하나만 제공하면 됨) 측면에서 정의되며 그 반대의 경우도 마찬가지입니다.

따라서 아마도 합법적이지 않은 Java에서는 다음과 같습니다.

interface Equal<T> {
    bool isEqual(T other) {
        return !isNotEqual(other); 
    }

    bool isNotEqual(T other) {
        return !isEqual(other); 
    }
}

작동 방식은 인터페이스를 구현하기 위해 이러한 메소드 중 하나만 제공하면된다는 것입니다. 따라서 인터페이스 수준 에서 원하는 동작의 일부를 부분적으로 구현할 수있는 능력이 차이라고 말하고 싶습니다 .


6

그것들은 비슷하고 (읽기 : 비슷한 용도로 사용됨) 유사하게 구현되었을 것입니다. Haskell의 다형성 함수는 형식 클래스와 관련된 함수를 나열하는 'vtable'을 내부적으로 취합니다.

이 테이블은 종종 컴파일 타임에 추론 될 수 있습니다. 이것은 아마도 Java에서는 사실이 아닙니다.

그러나 이것은 메서드가 아니라 함수 의 테이블입니다 . 메서드는 객체에 바인딩되어 있지만 Haskell 유형 클래스는 그렇지 않습니다.

자바의 제네릭처럼 보아라.


3

Daniel이 말했듯이 인터페이스 구현은 데이터 선언과 별도로 정의됩니다 . 그리고 다른 사람들이 지적했듯이 둘 이상의 장소에서 동일한 자유 유형을 사용하는 작업을 정의하는 간단한 방법이 있습니다. 그래서 정의하기 쉽습니다.Num 클래스 . 따라서 Haskell에서 우리는 실제로 어떤 매직 오버로드 연산자 없이도 연산자 오버로딩의 구문 적 이점을 얻습니다.

또 다른 차이점은 아직 그 유형의 구체적인 값이없는 경우에도 유형을 기반으로하는 메소드를 사용할 수 있다는 것입니다!

예 : read :: Read a => String -> a. 따라서 "읽기"결과를 사용하는 방법에 대한 다른 유형 정보가 충분하다면 컴파일러가 어떤 사전을 사용할지 알아낼 수 있습니다.

또한 읽을 수있는 모든 목록에 instance (Read a) => Read [a] where...대해 읽기 인스턴스를 정의 할 수 있는 것과 같은 작업을 수행 할 수 있습니다 . Java에서는 이것이 가능하지 않다고 생각합니다.

그리고이 모든 것은 속임수없이 표준 단일 매개 변수 타입 클래스 일뿐입니다. 다중 매개 변수 유형 클래스를 도입하면 완전히 새로운 가능성의 세계가 열리고, 유형 시스템에 훨씬 더 많은 정보와 계산을 임베드 할 수있는 기능적 종속성 및 유형 패밀리가 더 많이 열립니다.


1
다중 매개 변수 유형 클래스는 OOP에서 다중 디스패치하기 때문에 일반 유형 클래스는 인터페이스에 대한 것입니다. 프로그래밍 언어의 힘과 프로그래머의 골칫거리가 그에 따라 증가합니다.
CA McCann
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.