Java에서 "최종 클래스"의 요점은 무엇입니까?


569

Java에 관한 책을 읽고 있으며 전체 클래스를로 선언 할 수 있다고 말합니다 final. 나는 이것을 사용할 곳을 생각할 수 없다.

나는 프로그래밍에 익숙 하지 않으며 프로그래머가 실제로 프로그램에서 이것을 사용 하는지 궁금 합니다. . 그들이 사용하는 경우, 언제 사용하면 더 잘 이해하고 사용시기를 알 수 있습니다.

Java가 객체 지향적이고 클래스를 선언하면 final객체의 특성을 가진 클래스의 아이디어를 멈추지 않습니까?

답변:


531

우선, 나는이 기사를 추천합니다 : Java : 최종 클래스를 만들 때


그들이 사용하는 경우, 언제 사용하면 더 잘 이해하고 사용시기를 알 수 있습니다.

final클래스는 단순히 클래스 확장 할 수 없습니다 .

(이것은 클래스의 객체에 대한 모든 참조가 마치로 선언 된 것처럼 행동한다는 것을 의미하지는 않습니다 final.)

클래스를 final로 선언하는 것이 유용한 경우이 질문의 답변에서 다룹니다.

Java가 객체 지향적이고 클래스를 선언하면 final객체의 특성을 가진 클래스의 아이디어를 멈추지 않습니까?

어떤 의미에서는 그렇습니다.

클래스를 최종으로 표시하면 해당 코드 부분에 대해 강력하고 유연한 언어 기능이 비활성화됩니다. 일부 클래스는하지만, (그리고 어떤 경우에는 안 할 수 있는 좋은 방법에서 고려의 서브 클래스하도록 설계 할 수 없습니다)가. 이러한 경우 OOP를 제한하더라도 클래스를 최종 클래스로 표시하는 것이 좋습니다. 그러나 최종 클래스는 여전히 최종 클래스가 아닌 다른 클래스를 확장 할 수 있습니다.


39
답변에 추가하기 위해 Effective Java의 원칙 중 하나는 상속보다 구성을 선호하는 것입니다. 최종 키워드를 사용하면 해당 원칙을 시행하는 데 도움이됩니다.
Riggy

9
"당신은 주로 효율성과 보안상의 이유로합니다." 나는이 발언이 꽤 자주 들리지만 (Wikipedia도 언급하고 있음) 나는 여전히이 주장에 대한 추론을 이해하지 못한다. 누군가가 비 최종 java.lang.String이 어떻게 비효율적이거나 안전하지 않은지를 설명하고 싶어합니까?
MRA

27
@MRA String을 매개 변수로 받아들이는 메소드를 만들면 Strings가 있기 때문에 불변이라고 가정합니다. 결과적으로 String 객체에서 모든 메소드를 안전하게 호출 할 수 있으며 전달 된 String을 변경할 수 없다는 것을 알고 있습니다. String을 확장하고 실제 문자열을 변경하기 위해 하위 문자열의 구현을 변경하면 불변으로 예상되는 String 객체는 더 이상 불변이 아닙니다.
Cruncher

1
@Sortofabeginner 그리고 모든 String 메소드와 필드를 최종적으로 결정하자마자 추가 기능을 가진 클래스를 만들 수 있습니다 ... 그 시점에서 문자열과 클래스가있는 클래스를 만들 수도 있습니다 해당 문자열에서 작동하는 메소드를 작성하십시오.
Cruncher

1
@Shay final (다른 것들 중에서)은 객체를 불변으로 만드는 데 사용되므로 서로 관련이 없다고 말할 수는 없습니다. docs.oracle.com/javase/tutorial/essential/concurrency/…
Celeritas

184

Java에서 final 수정자가있는 변경할 수 없습니다!

여기에는 최종 클래스, 최종 변수 및 최종 메소드가 포함됩니다.

  • 마지막 수업은 다른 수업으로 확장 할 수 없습니다
  • 최종 변수는 다른 값을 재 할당 할 수 없습니다
  • 최종 방법은 재정의 할 수 없습니다

40
실제 질문은 무엇이 아니라 인가 입니다.
프란체스코 멘 자니

8
"Java에서 final수정자가있는 항목을 변경할 수 없습니다!" 라는 문구 는 너무 범주적이고 사실 완전히 정확하지 않습니다. Grady Booch가 말한 것처럼 "객체는 상태, 행동 및 정체성을 가지고 있습니다". 참조가 최종으로 표시된 후에는 개체의 ID를 변경할 수 없지만 필드 가 아닌 필드 (물론 제공되는 경우)에 새 값을 할당 하여 상태 를 변경할 final수 있습니다. 시험에서이 부분에 대한 질문이있을 수 있으므로 Oracle Java 인증 (1Z0-808 등)을 얻으려면이 점을 염두에 두어야합니다.
Igor Soudakevitch

33

보안상의 이유로 클래스 상속방지 하려는 경우 final이 중요한 시나리오 . 이를 통해 실행중인 코드를 다른 사람 이 재정의 할 수 없도록 할 수 있습니다 .

또 다른 시나리오는 최적화입니다 .Java 컴파일러는 최종 클래스에서 일부 함수 호출을 인라인한다는 것을 기억합니다. 따라서 호출 a.x()하고 a가 선언 final되면 컴파일 타임에 코드가 무엇인지 알고 호출 함수에 인라인 할 수 있습니다. 이것이 실제로 수행되는지 여부는 알 수 없지만 최종적으로는 가능합니다.


7
인라인은 일반적으로 런타임에 JIT (Just-In-Time) 컴파일러 만 수행합니다. 최종적으로도 작동하지 않지만 JIT 컴파일러는 확장 클래스가 없거나이 확장 클래스 가이 메소드를 건드리지 않도록하기 위해 약간 더 많은 작업이 있습니다.
Paŭlo Ebermann

인라인 및 최적화 문제에 대한 자세한 내용은 여기를 참조하십시오 : lemire.me/blog/archives/2014/12/17/…
Josh Hemann

24

가장 좋은 예는

공개 최종 클래스 문자열

불변 클래스이며 확장 할 수 없습니다. 물론, 클래스를 최종적으로 불변으로 만드는 것 이상이 있습니다.


Hehe는 때때로 Rube Goldergian 개발자를 자신으로부터 보호합니다.
Zoidberg

16

관련 자료 : Bob Martin 의 공개 원칙 .

주요 인용문 :

확장을 위해 소프트웨어 엔터티 (클래스, 모듈, 기능 등)가 열려 있어야하지만 수정을 위해 닫혀 있어야합니다.

final키워드는 방법이나 클래스에 사용되는 여부, 자바에서이를 시행 할 수있는 수단입니다.


6
@Sean : final클래스가 개방형이 아닌 확장을 위해 폐쇄되도록 선언하지 않습니까? 아니면 문자 그대로 복용합니까?
Goran Jovic

4
@Goran은 전 세계적으로 최종적으로 적용됩니다. 핵심은 수정을 원하지 않는 장소에서 최종적으로 선택적으로 적용하는 것입니다 (물론 확장을위한 좋은 고리를 제공합니다)
Sean Patrick Floyd

26
OCP에서 "수정"은 소스 코드 수정을 의미하고 "확장"은 구현 상속을 의미합니다. 따라서 final구현 코드를 수정을 위해 닫히고 상속을 통해 확장을 위해 열려는 경우 클래스 / 메서드 선언 에서 사용하는 것은 의미가 없습니다.
Rogério

1
@Rogerio Spring Framework Reference (MVC) 에서 참조 (및 해석)를 빌 렸습니다 . IMHO 이것은 원래 버전보다 훨씬 더 의미가 있습니다.
Sean Patrick Floyd

확장이 종료되었습니다. 쓸모없는. 사망했습니다. 파괴됨. 나는 OCP에 관심이 없다. 수업을 연장 할 변명의 여지가 없습니다.
Josh Woodcock

15

클래스 계층 구조를 Java와 같이 트리로 생각하면 추상 클래스는 분기 일 수 있고 최종 클래스는 리프 일 수있는 클래스입니다. 이러한 범주에 속하지 않는 수업은 가지와 잎이 될 수 있습니다.

여기서 OO 원칙을 위반하지 않으며 final은 단순히 훌륭한 대칭을 제공합니다.

실제로 객체를 불변으로 만들거나 API를 작성하는 경우 final을 사용하여 클래스의 확장이 아닌 API 사용자에게 신호를 보냅니다.


13

키워드 final자체는 최종적인 것을 의미하며 어떤 식 으로든 수정해서는 안됩니다. 클래스가 표시되어 있으면 final확장하거나 하위 클래스 로 만들 수 없습니다. 그러나 문제는 왜 우리가 수업을 표시 final합니까? IMO에는 여러 가지 이유가 있습니다.

  1. 표준화 : 일부 클래스는 표준 기능을 수행하며 문자열 조작 또는 수학 함수 등과 관련된 다양한 기능을 수행하는 클래스와 같이 수정되지 않습니다.
  2. 보안 이유 : 때로는 다양한 인증 및 암호 관련 기능을 수행하는 클래스를 작성하고 다른 사람이 변경하지 않기를 바랍니다.

나는 마킹 클래스가 final효율성을 향상 시킨다고 들었지만 솔직히 말해서 많은 무게를 지니고있는이 주장을 찾을 수 없었습니다.

Java가 객체 지향적이며 클래스를 final로 선언하면 객체의 특성을 가진 클래스의 아이디어를 멈추지 않습니까?

아마도 그렇습니다. 그러나 때때로 그것은 의도 된 목적입니다. 때때로 우리는이 클래스의 능력을 확장하여 보안 등의 더 큰 이점을 얻기 위해 그렇게합니다. 그러나 필요한 경우 최종 클래스는 여전히 하나의 클래스를 확장 할 수 있습니다.

참고로 상속보다 구성을 선호 해야 하며 final키워드는 실제로이 원칙을 시행하는 데 도움이됩니다.


6

수업을 "최종"으로 만들 때주의하십시오. 최종 클래스에 대한 단위 테스트를 작성하려는 경우 Michael C. Feathers의 저서 "레거시 코드로 효과적으로 작업하기"에 설명 된 "하위 클래스 및 재정의 방법"기술을 사용하기 위해이 최종 클래스를 서브 클래 싱 할 수 없습니다. . 이 책에서 Feathers는 "진실하게, 봉인과 최종은 잘못 된 실수이며, 프로그래밍 언어에 추가되어서는 안된다고 믿기 쉽습니다. 그러나 실제 결점은 우리에게 있습니다. 우리가 통제 할 수없는 도서관은 문제를 요구하고 있습니다. "


6

final class 새로운 메소드를 추가 할 때 퍼블릭 API가 깨지는 것을 피할 수 있습니다

Base클래스 의 버전 1에서 다음 을 수행 한다고 가정하십시오 .

public class Base {}

그리고 클라이언트는 :

class Derived extends Base {
    public int method() { return 1; }
}

그런 다음 버전 2에서 method메소드를 추가 하려면 Base다음 을 수행하십시오 .

class Base {
    public String method() { return null; }
}

클라이언트 코드가 손상 될 수 있습니다.

우리가 final class Base대신 사용했다면 , 클라이언트는 상속 할 수 없었고 메소드 추가는 API를 손상시키지 않을 것입니다.


5

클래스가로 표시 final되면 클래스 구조를 외부에서 수정할 수 없음을 의미합니다. 이것이 가장 눈에 띄는 곳은 전통적인 다형성 상속을 할 때 기본적으로 class B extends A작동하지 않습니다. 그것은 기본적으로 코드의 일부를 보호하는 방법 (범위) .

명확히하기 위해 클래스를 final표시하면 필드가 표시 final되지 않으므로 객체 속성이 아니라 실제 클래스 구조가 대신 보호됩니다.


1
객체 속성은 무엇을 의미합니까? 클래스가 final로 선언되면 클래스의 멤버 변수를 수정할 수 있다는 의미입니까? 따라서 최종 수업의 유일한 목적은 상속을 막는 것입니다.
Adam Lyu

5

최종 문제를 해결하려면 :

수업을 마무리하는 두 가지 방법이 있습니다. 첫 번째는 클래스 선언에서 final 키워드를 사용하는 것입니다.

public final class SomeClass {
  //  . . . Class contents
}

클래스를 final로 만드는 두 번째 방법은 모든 생성자를 private으로 선언하는 것입니다.

public class SomeClass {
  public final static SOME_INSTANCE = new SomeClass(5);
  private SomeClass(final int value) {
  }

마지막으로 표시하면이 테스트 클래스를 살펴 보는 것이 실제로 최종적인 것으로 밝혀지면 문제가 해결됩니다. 언뜻보기에 공개적으로 보입니다.

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

불행히도 클래스의 유일한 생성자는 private이므로이 클래스를 확장 할 수 없습니다. Test 클래스의 경우 클래스가 최종적인 이유는 없습니다. Test 클래스는 암시 적 최종 클래스가 어떻게 문제를 일으킬 수 있는지에 대한 좋은 예입니다.

따라서 암시 적으로 클래스를 생성자 전용으로 만들어서 클래스를 최종적으로 만들 때는 최종으로 표시해야합니다.


4

마지막 수업은 확장 할 수없는 수업입니다. 또한 서브 클래스로 대체 할 수 없음을 표시하기 위해 메소드를 final로 선언 할 수 있습니다.

API 또는 라이브러리를 작성하고 기본 동작을 변경하기 위해 확장되지 않도록하려는 경우 클래스의 서브 클래스 화를 방지하는 것이 특히 유용 할 수 있습니다.


4

클래스를 최종으로 유지하는 한 가지 장점 :-

String 클래스는 최종적으로 유지되므로 아무도 메서드를 재정의하고 기능을 변경할 수 없습니다. 예를 들어 아무도 length () 메소드의 기능을 변경할 수 없습니다. 항상 문자열의 길이를 반환합니다.

이 클래스의 개발자는 아무도이 클래스의 기능을 변경하기를 원하지 않았으므로 최종 클래스로 유지했습니다.


3

예, 때로는 보안 또는 속도상의 이유로이를 원할 수도 있습니다. C ++에서도 수행됩니다. 그것은하지 않을 수 있지만있는 moreso 프레임 워크를위한 프로그램에 적용. http://www.glenmccl.com/perfj_025.htm


3

자바에서 최종 키워드는 아래 경우에 사용됩니다.

  1. 최종 변수
  2. 최종 방법
  3. 최종 수업

자바에서 최종 변수는 재 할당 할 수없고, 최종 클래스 는 확장 될 수없고 최종 메소드는 재정의 할 수 없습니다.


1

최종 수업은 연장 할 수 없습니다. 따라서 클래스가 특정 방식으로 작동하고 누군가가 덜 효율적이고 악의적 인 코드로 메소드를 재정의하지 않도록하려면 전체 클래스를 원하지 않는 최종 또는 특정 메소드로 선언 할 수 있습니다 변경되었습니다.

클래스를 선언한다고해서 클래스가 인스턴스화되는 것을 막을 수는 없다고해서 클래스가 객체의 특성을 갖는 것을 막을 수는 없습니다. 단지 클래스에서 선언 된 방식대로 메소드를 고수해야한다는 것입니다.


1

FINAL을 "줄의 끝"이라고 생각하십시오. 그 남자는 더 이상 자손을 낳을 수 없습니다. 따라서 이런 식으로 볼 때, '줄 끝'마커를 클래스에 표시해야하는 수많은 실제 시나리오가 있습니다. 도메인 기반 디자인입니다. 도메인에서 지정된 ENTITY (클래스)가 서브 클래스를 작성할 수 없도록 요구하는 경우이를 FINAL로 표시하십시오.

"최종 태그"클래스를 상속받지 못하게하는 것은 없습니다. 그러나 그것은 일반적으로 "상속의 남용"으로 분류되며, 대부분의 경우 클래스의 기본 클래스에서 일부 기능을 상속하려고하기 때문에 수행됩니다.

가장 좋은 방법은 도메인을보고 디자인 결정을 지시하는 것입니다.


1

위에서 언급했듯이 아무도 메소드의 기능을 변경할 수 없도록하려면 final로 선언 할 수 있습니다.

예 : 다운로드 / 업로드 용 응용 프로그램 서버 파일 경로, 오프셋을 기준으로 문자열 분할 등의 메서드 이러한 메서드 함수는 변경되지 않도록 Final로 선언 할 수 있습니다. 그리고 별도의 클래스에서 이러한 최종 메서드를 원하면 해당 클래스를 Final 클래스로 정의하십시오. 따라서 Final 클래스에는 Final 메서드가 최종 클래스가 아닌 클래스에서 선언 및 정의 될 수있는 모든 최종 메서드가 있습니다.



1

Employee메소드 가있는 클래스가 있다고 가정 해 봅시다 greet. greet메소드가 호출 되면 단순히 인쇄합니다 Hello everyone!. 이것이 방법 의 예상되는 동작 입니다greet

public class Employee {

    void greet() {
        System.out.println("Hello everyone!");
    }
}

이제 아래와 같이 GrumpyEmployee서브 클래스 Employeegreet메소드를 오버라이드 하자 .

public class GrumpyEmployee extends Employee {

    @Override
    void greet() {
        System.out.println("Get lost!");
    }
}

이제 아래 코드에서 sayHello메소드를 살펴보십시오 . 그것은 소요 Employee매개 변수로 인스턴스를하고 말 것이라고 기대하는 인사 메소드를 호출 Hello everyone!하지만 우리가 얻을 것은 Get lost!. 이 행동의 변화는Employee grumpyEmployee = new GrumpyEmployee();

public class TestFinal {
    static Employee grumpyEmployee = new GrumpyEmployee();

    public static void main(String[] args) {
        TestFinal testFinal = new TestFinal();
        testFinal.sayHello(grumpyEmployee);
    }

    private void sayHello(Employee employee) {
        employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
    }
}

이 상황은 수업이 이루어진 경우 피할 수 있습니다 . 클래스가로 선언되지 않은 경우 건방진 프로그래머가 발생할 수있는 혼란의 정도를 상상해보십시오 .EmployeefinalStringfinal


1

최종 수업은 더 이상 연장 할 수 없습니다. 자바에서 클래스를 상속받을 필요가 없다면이 접근법을 사용할 수 있습니다.

오버라이드되지 않도록 클래스의 특정 메소드를 작성해야하는 경우 최종 키워드를 앞에 추가 할 수 있습니다. 클래스는 여전히 상속 가능합니다.


-1

객체 지향은 상속에 관한 것이 아니라 캡슐화에 관한 것입니다. 그리고 상속은 캡슐화를 깨뜨립니다.

클래스 파이널 선언은 많은 경우에 완벽합니다. 색상이나 금액과 같은“가치”를 나타내는 모든 물체는 최종적 일 수 있습니다. 그들은 스스로 서 있습니다.

라이브러리를 작성하는 경우 클래스를 명시 적으로 들여 쓰지 않으면 클래스를 최종 클래스로 만듭니다. 그렇지 않으면 사람들이 클래스를 파생시키고 메소드를 재정 의하여 가정 / 불변을 깨뜨릴 수 있습니다. 보안에 영향을 줄 수도 있습니다.

"Effective Java"의 Joshua Bloch는 상속을 위해 명시 적으로 디자인하거나이를 금지 할 것을 권장하며 상속을 위해 디자인하는 것은 쉽지 않다고 지적합니다.

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