Java에서 상속을 금지해야하는 좋은 이유는 무엇입니까?


178

Java에서 상속을 금지해야하는 이유는 무엇입니까 (예 : 단일 개인용 매개 변수없는 생성자를 사용하는 최종 클래스 또는 클래스 사용)? 방법을 최종적으로 만드는 좋은 이유는 무엇입니까?


1
Pedantry : 기본 생성자는 코드에 명시 적으로 작성하지 않으면 Java 컴파일러가 생성 한 것입니다. 인수 없음 생성자를 의미합니다.
John Topley

"암시 적 기본 생성자"아닌가요?
cretzel

2
"클래스에 생성자를 제공 할 필요는 없지만이를 수행 할 때는주의해야합니다. 컴파일러는 생성자가없는 클래스에 대해 인수없는 기본 생성자를 자동으로 제공합니다." java.sun.com/docs/books/tutorial/java/javaOO/constructors.html – John Topley
John Topley

답변:


184

여기에서 가장 잘 언급 할 것은 Joshua Bloch의 훌륭한 책 "Effective Java"의 항목 19이며, "상속을위한 디자인 및 문서 또는 금지"입니다. (제 2 판의 17 번 항목과 제 1 판의 15 번 항목입니다.) 실제로 읽어야하지만 요약하겠습니다.

상속 된 클래스와 부모와의 상호 작용은 조상이 상속되도록 설계되지 않은 경우 놀랍고 실행 불가능합니다.

그러므로 수업은 두 종류로 이루어져야합니다.

  1. 확장 되도록 고안된 클래스 및 수행 방법을 설명하기위한 충분한 문서

  2. 최종으로 표시된 수업

순전히 내부 코드를 작성하는 경우 약간 과잉 일 수 있습니다. 그러나 클래스 파일에 5자를 추가하는 데 드는 추가 노력은 매우 적습니다. 내부 소비에 대해서만 작성하는 경우 미래 ​​코더는 항상 '최종'을 제거 할 수 있습니다. "이 클래스는 상속을 염두에두고 설계되지 않았다"는 경고로 생각할 수 있습니다.


6
내 경험상 이것은 나와 다른 사람이 코드를 사용한다면 과도하지 않습니다. Java가 기본적으로 메소드를 대체 할 수있는 이유를 이해하지 못했습니다.
Eric Weilnau

9
Effective Java의 일부를 이해하려면 Josh의 디자인에 대한 이해를 이해해야합니다. 그는 클래스 인터페이스를 마치 공용 API 인 것처럼 항상 디자인해야한다고 말합니다. 대부분의 의견은이 접근법이 일반적으로 너무 무겁고 융통성이 없다는 것입니다. (YAGNI 등)
Tom Hawtin-tackline

9
좋은 대답입니다. (공백도 있으므로 6
자임을

36
클래스를 마지막으로 만들면 테스트가 더 어려워 질 수 있습니다 (스텁 또는 모의하기가 더 어렵습니다)
notnoop

10
마지막 클래스가 인터페이스에 쓰고 있다면 조롱은 문제가되지 않습니다.
Fred Haslam

26

클래스를 재정의하면 다른 메서드에서 계산되는 동작을 변경할 수 없도록 메서드를 최종적으로 만들 수 있습니다. 생성자에서 호출 된 메소드는 종종 final로 선언되므로 객체를 만들 때 불쾌한 놀라움을 느끼지 않습니다.


이 답변을 이해하지 못했습니다. 마음에 들지 않으면 "클래스를 재정의하면 다른 방법으로 계산되는 동작을 변경할 수 없습니다"라는 의미를 설명 할 수 있습니까? 문장을 이해하지 못했기 때문에 먼저 묻고, 넷빈이 생성자에서 호출하는 함수 중 하나가이어야한다고 제안했기 때문에 묻습니다 final.
Nav

1
@Nav 메소드를 final로 만들면 재정의 클래스는 해당 메소드의 자체 버전을 가질 수 없거나 컴파일 오류가 발생합니다. 이렇게하면 다른 사람이 클래스를 확장 할 때 해당 메서드에서 구현 한 동작이 나중에 변경되지 않는다는 것을 알 수 있습니다.
Bill the Lizard

19

클래스를 마지막으로 만드는 한 가지 이유는 상속보다 구성을 강요하려는 경우입니다. 이것은 일반적으로 클래스 간의 긴밀한 연결을 피하는 데 바람직합니다.


15

최종 방법으로 갈 수있는 3 가지 사용 사례가 있습니다.

  1. 파생 클래스가 특정 기본 클래스 기능을 재정의하지 않도록합니다.
  2. 이것은 기본 클래스가 파생 클래스가 변경하지 않아야하는 프레임 워크의 중요한 핵심 기능을 제공하는 보안 목적입니다.
  3. 최종 및 개인 메소드에 가상 테이블 개념을 사용하지 않으므로 최종 메소드는 인스턴스 메소드보다 빠릅니다. 따라서 가능한 경우에는 최종 방법을 사용하십시오.

수업을 마무리하기위한 목적 :

어떤 신체도 그러한 클래스를 확장하고 그들의 행동을 바꿀 수 없습니다.

예 : 래퍼 클래스 정수는 최종 클래스입니다. 해당 클래스가 최종 클래스가 아닌 경우 어느 누구도 Integer를 자신의 클래스로 확장하고 정수 클래스의 기본 동작을 변경할 수 있습니다. 이를 피하기 위해 java는 모든 래퍼 클래스를 최종 클래스로 만들었습니다.


당신의 모범에 대해서는 크게 확신하지 못했습니다. 그렇다면 클래스 전체를 최종적으로 만드는 대신에 메소드와 변수를 최종으로 만드는 것이 어떻습니까? 세부 사항을 사용하여 답변을 편집 할 수 있습니까?
Rajkiran

4
모든 변수와 메소드를 최종으로 만들더라도 다른 클래스는 클래스를 상속하고 추가 기능을 추가하고 클래스의 기본 동작을 변경할 수 있습니다.
user1923551

3
> 해당 클래스가 최종 클래스가 아닌 경우 어느 누구도 Integer를 자신의 클래스로 확장하고 정수 클래스의 기본 동작을 변경할 수 있습니다. 이것이 허용 되고이 새로운 클래스가 호출 DerivedInteger되었다고 가정하면 여전히 원래 Integer 클래스를 변경하지 않으며 사용하는 사람은 DerivedInteger스스로 위험을 감수해야하므로 여전히 왜 문제인지 알 수 없습니다.
Siddhartha


7

상속은 전기 톱과 같습니다-매우 강력하지만 잘못된 손에는 끔찍합니다. 상속 할 클래스를 설계하거나 (유연성을 제한하고 더 오래 걸릴 수 있음) 금지해야합니다.

Effective Java 2nd edition items 16 and 17 또는 내 블로그 게시물 "Inheritance Tax"를 참조하십시오 .


블로그 게시물에 대한 링크가 끊어졌습니다. 또한 이것에 대해 좀 더 자세히 설명 할 수 있다고 생각하십니까?

@MichaelT : 링크가 고정, 감사 - (난 두려워, 나는 지금 여기에 추가 할 시간이 없어.) : 더 정교 그것을 따라
존 소총을

@ JonSkeet, 블로그 게시물에 대한 링크가 손상되었습니다.
Lucifer

@Kedarnath : 그것은 나를 위해 작동 ...이 되었다 codeblog.jonskeet.uk에, 잠시 동안 깨진하지만 지금 올바른 페이지를 가리키는 것 ...
존 소총

4

흠 ... 두 가지를 생각할 수 있습니다.

특정 보안 문제를 다루는 클래스가있을 수 있습니다. 공격자는이를 하위 분류하고 시스템의 하위 분류 버전을 시스템에 공급함으로써 보안 제한을 우회 할 수 있습니다. 예를 들어 응용 프로그램에서 플러그인을 지원할 수 있으며, 플러그인이 보안 관련 클래스를 서브 클래 싱 할 수있는 경우이 트릭을 사용하여 서브 클래스 버전의 플러그인을 제자리에 밀입시킬 수 있습니다. 그러나 이것은 애플릿 등과 관련하여 Sun이 처리해야하는 문제 일 수 있습니다.

훨씬 더 현실적인 것은 객체가 변경 가능한 것을 피하는 것입니다. 예를 들어 문자열은 변경할 수 없으므로 코드에서 안전하게 참조를 유지할 수 있습니다

 String blah = someOtherString;

먼저 문자열을 복사하는 대신. 그러나 문자열을 서브 클래 싱 할 수있는 경우 문자열 값을 수정할 수있는 메소드를 추가 할 수 있습니다. 이제는 위와 같이 문자열을 복사하면 문자열이 동일하게 유지되는 코드를 더 이상 신뢰할 수 없습니다. 끈.


2

또한 상용 폐쇄 소스 클래스를 작성하는 경우, 특히 지원을 제공해야하고 사람들이 메소드를 대체하고 호출이 제공한다고 불평하는 경우, 사람들이 기능을 변경하지 못하게 할 수 있습니다. 예기치 않은 결과.


2

클래스와 메서드를 final로 표시하면 런타임에서 지정된 개체를 호출하기 위해 올바른 클래스 메서드를 찾을 필요가 없으므로 성능이 약간 향상 될 수 있습니다. 비 최종 메소드는 가상으로 표시되어 필요한 경우 적절하게 확장 할 수 있으며 최종 메소드는 클래스에서 인라인으로 직접 링크하거나 컴파일 할 수 있습니다.


1
현재 세대의 VM이 필요에 따라 최적화 및 최적화 해제 할 수 있어야하므로 이것이 신화라고 생각합니다. 나는 이것을 벤치마킹하고 신화로 분류하는 것을 본 것을 기억합니다.
Miguel Ping

1
코드 생성의 차이는 신화가 아니지만 성능이 향상 될 수 있습니다. 작은 게인이라고 말한 것처럼 한 사이트는 3.5 %의 성능 향상을 언급했는데 일부는 더 높았습니다. 대부분의 경우 코드에서 나치를 모두 이용할 가치는 없습니다.
seanalltogether

1
신화가 아닙니다. 실제로 컴파일러는 V 최종 메소드 만 인라인 할 수 있습니다. JIT가 "인라인"일인지 아트 런타임인지 확실하지 않지만 나중에 파생 클래스를로드 할 수 있기 때문에 그것이 가능하다는 것을 의심합니다.
Uri

1
마이크로 벤치 마크 == 소금 알갱이. 즉, 10B 사이클 워밍업 후 10B 이상의 평균 호출은 랩톱의 Eclipse에서 본질적으로 차이가 없음을 보여줍니다. 문자열을 반환하는 두 가지 방법, final은 6.9643us, 비 최종은 6.9641us입니다. 그 델타는 배경 소음입니다. IMHO.
joel.neely

1

사람들이 자신과 다른 사람을 혼란스럽게 할 수있는 일을하지 못하게합니다. 상수 또는 계산이 정의 된 물리 라이브러리를 상상해보십시오. 최종 키워드를 사용하지 않으면 누군가가 와서 절대로 변경해서는 안되는 기본 계산이나 상수를 재정의 할 수 있습니다.


2
그 주장에 대해 이해할 수없는 것이 있습니다. 누군가 변경해서는 안되는 계산 / 상수를 변경 한 경우-그 100 %의 결함으로 인한 실패가 아닙니까? 다시 말해, 변화가 어떤 혜택을 얻는가?
matt b

예, 그것은 기술적으로 "그들의"결함이지만 변경 사항을 알지 못하는 라이브러리를 사용하는 다른 누군가가 혼란을 초래할 수 있다고 상상하십시오. 나는 사람들이 기본 기능을 변경하지 못하도록 많은 Java Base 클래스가 최종으로 표시되는 이유라고 항상 생각했습니다.
Anson Smith

@ matt b-당신은 변경을 한 개발자가 의도적으로 그렇게했거나 그것이 자신의 잘못이라고 신경 쓰겠다고 가정하는 것 같습니다. 나와 다른 사람이 내 코드를 사용하는 경우 변경하지 않는 한 최종 코드로 표시합니다.
Eric Weilnau

이것은 실제로 요청한 것과 '최종'의 다른 사용법입니다.
DJClayworth

이 답변은 개별 필드의 상속 및 변경 가능성을 서브 클래스 작성 기능과 혼동하는 것으로 보입니다. 상속을 금지하지 않고 개별 필드 및 메소드를 최종으로 정의 (재정의 방지) 할 수 있습니다.
joel.neely

0

클래스를 재정의해도 동작이 변경되지 않도록 메서드를 최종적으로 만들고 싶습니다. 동작을 변경하고 싶을 때 메소드를 공개하십시오. 공개 메소드를 대체하면 변경할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.