Java 및 C #에서는 다중 상속이 허용되지 않는다는 것을 알고 있습니다. 많은 책에서는 다중 상속이 허용되지 않는다고 말합니다. 그러나 인터페이스를 사용하여 구현할 수 있습니다. 허용되지 않는 이유에 대해서는 논의되지 않습니다. 아무도 그것이 허용되지 않는 이유를 정확하게 말할 수 있습니까?
Java 및 C #에서는 다중 상속이 허용되지 않는다는 것을 알고 있습니다. 많은 책에서는 다중 상속이 허용되지 않는다고 말합니다. 그러나 인터페이스를 사용하여 구현할 수 있습니다. 허용되지 않는 이유에 대해서는 논의되지 않습니다. 아무도 그것이 허용되지 않는 이유를 정확하게 말할 수 있습니까?
답변:
짧은 대답은 언어 디자이너가하지 않기로 결정했기 때문입니다.
기본적으로 .NET 및 Java 디자이너 모두 MI를 추가 하면 언어 에 너무 많은 복잡성 이 추가 되는 반면 너무 적은 이점 을 제공 한다고 생각했기 때문에 다중 상속을 허용하지 않는 것 같습니다 .
좀 더 재미 있고 깊이있게 읽을 수 있도록 웹에서 일부 언어 디자이너의 인터뷰와 함께 사용할 수있는 기사가 있습니다. 예를 들어, .NET의 경우, CLR 관련 MS에서 일한 Chris Brumme는 다음과 같은 이유를 설명했습니다.
서로 다른 언어는 실제로 MI 작동 방식에 대해 서로 다른 기대치를 가지고 있습니다. 예를 들어, 충돌이 해결되는 방법과 중복 기반이 병합 또는 중복되는지 여부가 있습니다. CLR에서 MI를 구현하기 전에 모든 언어를 조사하고 공통 개념을 파악하고 언어 중립적 인 방식으로 표현하는 방법을 결정해야합니다. 또한 MI가 CLS에 속하는지 여부와이 개념을 원하지 않는 언어 (예 : VB.NET)에서 이것이 의미하는 바를 결정해야합니다. 물론 이것이 우리가 공통 언어 런타임으로하고있는 사업이지만 아직 MI를 위해 수행하지는 않았습니다.
MI가 진정으로 적절한 곳의 수는 실제로 매우 적습니다. 대부분의 경우 다중 인터페이스 상속이 대신 작업을 수행 할 수 있습니다. 다른 경우에는 캡슐화 및 위임을 사용할 수 있습니다. 믹스 인과 같은 약간 다른 구조를 추가하면 실제로 더 강력할까요?
다중 구현 상속은 구현에 많은 복잡성을 주입합니다. 이러한 복잡성은 캐스팅, 레이아웃, 디스패치, 필드 액세스, 직렬화, ID 비교, 검증 가능성, 반사, 제네릭 및 기타 여러 장소에 영향을 미칩니다.
Java의 경우 다음 기사를 읽을 수 있습니다 .
Java 언어에서 다중 상속을 생략하는 이유는 대부분 "단순하고 객체 지향적이며 친숙한"목표에서 비롯됩니다. 단순한 언어로서 Java의 제작자는 대부분의 개발자가 광범위한 교육없이 이해할 수있는 언어를 원했습니다. 이를 위해 그들은 C ++의 불필요한 복잡성 (단순함)을 넘기지 않고 가능한 한 C ++와 유사한 언어 (익숙한)를 만들기 위해 노력했습니다.
디자이너의 의견으로는 다중 상속은 해결하는 것보다 더 많은 문제와 혼란을 야기합니다. 그래서 그들은 (연산자 과부하를 줄이는 것처럼) 언어에서 다중 상속을 자릅니다. 디자이너의 광범위한 C ++ 경험을 통해 다중 상속은 골칫거리가 아니라는 사실을 알게되었습니다.
구현의 다중 상속은 허용되지 않습니다.
문제는 컴파일러 / 런타임이 Draw () 메서드에 대한 구현이있는 Cowboy 및 Artist 클래스가있는 경우 수행 할 작업을 파악할 수 없다는 것입니다. 그런 다음 새 CowboyArtist 형식을 만들려고합니다. draw () 메서드를 호출하면 어떻게됩니까? 누군가가 거리에서 죽어 있나요, 아니면 아름다운 수채화가 있나요?
이중 다이아몬드 상속 문제라고 생각합니다.
이유 : Java는 단순성 때문에 매우 인기 있고 코딩하기 쉽습니다.
그래서 자바 개발자들은 프로그래머가 이해하기 어렵고 복잡하다고 느끼는 것을 피하려고 노력했습니다. 이러한 종류의 속성 중 하나는 다중 상속입니다.
다중 상속 문제 : 다이아몬드 문제.
예 :
이것은 다이아몬드 문제에 존재하는 모호성입니다.
이 문제를 해결하는 것은 불가능하지 않지만, 읽는 동안 프로그래머에게 더 많은 혼란과 복잡성을 야기합니다. 해결하려고하는 것보다 더 많은 문제를 일으 킵니다.
참고 : 그러나 어떤 방법 으로든 인터페이스를 사용하여 항상 간접적으로 다중 상속을 구현할 수 있습니다.
Java는 C ++와 크게 다른 디자인 철학을 가지고 있기 때문입니다. (여기서 C #은 논의하지 않겠습니다.)
C ++를 설계 할 때 Stroustrup은 어떻게 오용 될 수 있는지에 관계없이 유용한 기능을 포함하기를 원했습니다. 다중 상속, 연산자 오버로딩, 템플릿 및 기타 다양한 기능을 사용하여 큰 시간을 낭비 할 수 있지만이를 사용하여 아주 좋은 작업을 수행 할 수도 있습니다.
Java 디자인 철학은 언어 구조의 안전성을 강조하는 것입니다. 그 결과 훨씬 더 어색한 일이 있지만,보고있는 코드가 자신이 생각하는 일을 의미한다는 사실을 훨씬 더 확신 할 수 있습니다.
게다가 자바는 가장 잘 알려진 OO 언어 인 C ++와 스몰 토크의 반응이었다. MI를 더 잘 처리하는 다른 OO 시스템과 함께 많은 다른 OO 언어 (Common Lisp가 실제로 표준화 된 첫 번째 언어)가 있습니다.
인터페이스, 구성 및 위임을 사용하여 Java에서 MI를 수행하는 것이 전적으로 가능하다는 것은 말할 것도 없습니다. C ++보다 더 명시 적이므로 사용하기 어색하지만 언뜻보기에 이해하기 쉬운 내용을 얻을 수 있습니다.
여기에는 정답이 없습니다. 다양한 답변이 있으며 주어진 상황에 대해 어느 것이 더 나은지는 응용 프로그램과 개인의 선호도에 따라 다릅니다.
다중 상속은
따라서 Java 언어에 다중 상속을 포함 하지 않는 것이 현명한 선택으로 간주 될 수 있습니다 .
컴퓨터 과학이 과학에 가깝고 대량 생산이 적었던 옛날 (70 년대) 프로그래머는 좋은 디자인과 좋은 구현에 대해 생각할 시간을 가졌고 그 결과 제품 (프로그램)이 고품질 (예 : TCP / IP 디자인)을 가졌습니다. 및 구현). 요즘은 모두가 프로그래밍하고 관리자가 마감일 전에 사양을 변경하고있을 때 Steve Haigh 게시물의 위키피디아 링크에 설명 된 것과 같은 미묘한 문제를 추적하기가 어렵습니다. 따라서 "다중 상속"은 컴파일러 설계에 의해 제한됩니다. 당신이 그것을 좋아한다면, 당신은 여전히 C ++를 사용할 수 있습니다 .... 그리고 당신이 원하는 모든 자유를 가질 수 있습니다 :)
나는 "자바에서는 다중 상속이 허용되지 않는다"는 말을 조금만 들었다.
다중 상속은 "유형"이 둘 이상의 "유형"에서 상속 될 때 정의됩니다. 또한 인터페이스는 동작이 있으므로 유형으로 분류됩니다. 따라서 Java에는 다중 상속이 있습니다. 더 안전합니다.
클래스의 동적로드는 다중 상속 구현을 어렵게 만듭니다.
Java에서는 실제로 단일 상속과 인터페이스를 사용하여 다중 상속의 복잡성을 피했습니다. 다음과 같은 상황에서 다중 상속의 복잡성이 매우 높습니다.
다중 상속의 다이아몬드 문제. A에서 상속 된 두 개의 클래스 B와 C가 있습니다. B와 C가 상속 된 메서드를 재정의하고 자체 구현을 제공한다고 가정합니다. 이제 D는 B와 C 모두에서 다중 상속을 수행합니다. D는 재정의 된 메서드를 상속해야합니다. jvm이 어떤 재정의 된 메서드를 사용할지 결정할 수 없습니까?
C ++에서 가상 함수는 처리하는 데 사용되며 명시 적으로 수행해야합니다.
이것은 인터페이스를 사용하여 피할 수 있으며 메서드 본문이 없습니다. 인터페이스는 인스턴스화 할 수 없으며 클래스로만 구현하거나 다른 인터페이스로 확장 할 수 있습니다.
자바에는 개념, 즉 다형성이 있습니다. Java에는 두 가지 유형의 다형성이 있습니다. 메서드 오버로딩과 메서드 재정의가 있습니다. 그중 메서드 재정의는 수퍼 클래스 및 하위 클래스 관계에서 발생합니다. 서브 클래스의 객체를 생성하고 수퍼 클래스의 메소드를 호출하고 서브 클래스가 둘 이상의 클래스를 확장하는 경우 어떤 수퍼 클래스 메소드를 호출해야합니까?
또는으로 슈퍼 클래스 생성자를 호출하는 동안 super()
어떤 슈퍼 클래스 생성자가 호출됩니까?
이 결정은 현재 Java API 기능으로 불가능합니다. 따라서 Java에서는 다중 상속이 허용되지 않습니다.
다중 상속은 Java에서 직접 허용되지 않지만 인터페이스를 통해 허용됩니다.
이유 :
다중 상속 : 더 복잡하고 모호함을 도입합니다.
인터페이스 : 인터페이스는 공개적으로 사용 가능한 인터페이스에서 프로그램의 구조 또는 내부 작업을 적절하게 설명하는 일관된 방법을 제공하는 Java의 완전히 추상적 인 클래스이며, 그 결과 더 많은 유연성과 재사용 가능한 코드 및 더 많은 제어가 가능합니다. 다른 클래스를 만들고 상호 작용하는 방법에 대해
좀 더 정확하게 말하자면, 그것들은 하나 이상의 클래스로 업 캐스트 될 수있는 클래스들과 같이 일종의 다중 상속을 수행 할 수있게 해주는 추가 특성을 가진 Java의 특별한 구조입니다.
간단한 예를 들어 보겠습니다.
메서드 이름은 같지만 기능이 다른 두 개의 슈퍼 클래스 클래스 A와 B가 있다고 가정합니다. (확장) 키워드로 다음 코드를 통해 다중 상속이 불가능합니다.
public class A
{
void display()
{
System.out.println("Hello 'A' ");
}
}
public class B
{
void display()
{
System.out.println("Hello 'B' ");
}
}
public class C extends A, B // which is not possible in java
{
public static void main(String args[])
{
C object = new C();
object.display(); // Here there is confusion,which display() to call, method from A class or B class
}
}
그러나 인터페이스를 통해 키워드로 (구현) 다중 상속이 가능합니다.
interface A
{
// display()
}
interface B
{
//display()
}
class C implements A,B
{
//main()
C object = new C();
(A)object.display(); // call A's display
(B)object.display(); //call B's display
}
}
아무도 그것이 허용되지 않는 이유를 정확하게 말할 수 있습니까?
이 문서 링크 에서 답변을 찾을 수 있습니다.
Java 프로그래밍 언어가 둘 이상의 클래스를 확장 할 수없는 한 가지 이유는 여러 클래스에서 필드를 상속하는 기능인 상태의 다중 상속 문제를 방지하기위한 것입니다.
다중 상속이 허용되고 해당 클래스를 인스턴스화하여 개체를 만들 때 해당 개체는 모든 클래스의 슈퍼 클래스에서 필드를 상속합니다. 두 가지 문제가 발생합니다.
다른 슈퍼 클래스의 메서드 나 생성자가 동일한 필드를 인스턴스화하면 어떻게됩니까?
어떤 메서드 또는 생성자가 우선할까요?
이제 상태의 다중 상속이 허용되지만 여전히 구현할 수 있습니다.
유형의 다중 상속 : 클래스가 둘 이상의 인터페이스를 구현하는 능력.
구현의 다중 상속 (인터페이스의 기본 메서드를 통해) : 여러 클래스에서 메서드 정의를 상속하는 기능
추가 정보는이 관련 SE 질문을 참조하십시오.
C ++에서 클래스는 둘 이상의 클래스에서 직접 또는 간접적으로 상속 할 수 있습니다 .이를 다중 상속 이라고합니다 .
그러나 C # 및 Java 는 각 클래스가 단일 부모 클래스에서 상속 하는 단일 상속으로 클래스를 제한 합니다.
다중 상속은 단일 애플리케이션 내에서 서로 다른 클래스 프레임 워크를 사용할 때 종종 발생하는 두 개의 서로 다른 클래스 계층의 측면을 결합하는 클래스를 만드는 유용한 방법입니다.
예를 들어 두 개의 프레임 워크가 예외에 대한 자체 기본 클래스를 정의하는 경우 다중 상속을 사용하여 두 프레임 워크에서 사용할 수있는 예외 클래스를 만들 수 있습니다.
다중 상속의 문제점은 모호함으로 이어질 수 있다는 것입니다. 고전적인 예는 한 클래스가 다른 두 클래스에서 상속되는 경우이며 각 클래스는 동일한 클래스에서 상속됩니다.
class A {
protected:
bool flag;
};
class B : public A {};
class C : public A {};
class D : public B, public C {
public:
void setFlag( bool nflag ){
flag = nflag; // ambiguous
}
};
이 예에서 flag
데이터 멤버는에 의해 정의됩니다 class A
. 그러나 둘 다에서 파생 class D
되는 class B
및의 자손 이므로의 두 인스턴스가 의 클래스 계층 구조 에 있기 때문에 본질적으로 두 개의 복사본 을 사용할 수 있습니다 . 어느 것을 설정 하시겠습니까? 컴파일러는 in에 대한 참조 가 모호 하다고 불평 합니다 . 한 가지 수정 사항은 참조를 명시 적으로 명확하게하는 것입니다.class C
A
flag
A
D
flag
D
B::flag = nflag;
또 다른 수정 사항은 B와 C를로 선언 virtual base classes
하는 것입니다. 즉, A의 복사본 하나만 계층 구조에 존재할 수 있으므로 모호함이 제거됩니다.
파생 개체가 생성 될 때 기본 클래스가 초기화되는 순서 또는 파생 클래스에서 멤버를 실수로 숨길 수있는 방법과 같이 다중 상속에는 다른 복잡성이 있습니다. 이러한 복잡성을 피하기 위해 일부 언어는 더 단순한 단일 상속 모델로 제한됩니다.
이것은 상속을 상당히 단순화하지만 공통 조상을 가진 클래스 만 동작을 공유 할 수 있기 때문에 유용성을 제한합니다. 인터페이스는 서로 다른 계층 구조의 클래스가 코드를 공유하여 구현되지 않더라도 공통 인터페이스를 노출하도록 허용하여 이러한 제한을 다소 완화합니다.
이 예를 상상해보십시오 : 수업이 있습니다 Shape1
그것은이 CalcualteArea
방법을 :
Class Shape1
{
public void CalculateArea()
{
//
}
}
Shape2
동일한 방법을 가진 또 다른 클래스 가 있습니다.
Class Shape2
{
public void CalculateArea()
{
}
}
이제 Circle 자식 클래스가 있는데 Shape1과 Shape2에서 파생됩니다.
public class Circle: Shape1, Shape2
{
}
이제 Circle에 대한 객체를 생성하고 메서드를 호출 할 때 시스템이 호출 할 영역 계산 메서드를 알지 못합니다. 둘 다 동일한 서명이 있습니다. 따라서 컴파일러는 혼란 스러울 것입니다. 이것이 다중 상속이 허용되지 않는 이유입니다.
그러나 인터페이스에는 메서드 정의가 없기 때문에 여러 인터페이스가있을 수 있습니다. 두 인터페이스 모두 동일한 메서드를 가지고 있어도 둘 다 구현이 없으며 항상 자식 클래스의 메서드가 실행됩니다.