[java] Java에서 변경 불가능한 클래스를 final로 선언하는 이유는 무엇입니까?

Java에서 클래스를 변경 불가능 하게 만들 려면 다음을 수행해야합니다.

  1. 세터를 제공하지 마십시오.
  2. 모든 필드를 비공개로 표시
  3. 수업 최종 결정

3 단계가 필요한 이유는 무엇입니까? 수업에 왜 표시를해야 final합니까?



답변

클래스를 표시하지 않으면 final갑자기 변경 불가능 해 보이는 클래스를 실제로 변경 가능하게 만들 수 있습니다. 예를 들어 다음 코드를 고려하십시오.

public class Immutable {
     private final int value;

     public Immutable(int value) {
         this.value = value;
     }

     public int getValue() {
         return value;
     }
}

이제 다음을 수행한다고 가정합니다.

public class Mutable extends Immutable {
     private int realValue;

     public Mutable(int value) {
         super(value);

         realValue = value;
     }

     public int getValue() {
         return realValue;
     }
     public void setValue(int newValue) {
         realValue = newValue;
     }

    public static void main(String[] arg){
        Mutable obj = new Mutable(4);
        Immutable immObj = (Immutable)obj;
        System.out.println(immObj.getValue());
        obj.setValue(8);
        System.out.println(immObj.getValue());
    }
}

Mutable하위 클래스 에서 내 하위 클래스에 getValue선언 된 변경 가능한 새 필드를 읽기 위해 의 동작을 재정의했습니다 . 결과적으로 처음에는 변경 불가능 해 보이는 클래스가 실제로 변경 불가능하지 않습니다. 객체가 예상되는 Mutable곳에이 객체 를 전달할 수 있으며 Immutable, 객체가 진정으로 불변이라고 가정하여 코드에 매우 나쁜 일을 할 수 있습니다. 기본 클래스를 표시 final하면 이런 일이 발생하지 않습니다.

도움이 되었기를 바랍니다!


답변

많은 사람들이 믿는 것과는 달리 불변 클래스를 만들 필요 final없습니다 .

불변 클래스를 만들기위한 표준 인수 final 이렇게하지 않으면 하위 클래스가 변경 가능성을 추가하여 수퍼 클래스의 계약을 위반할 수 있다는 것입니다. 클래스의 클라이언트는 불변성을 가정하지만 그 아래에서 무언가가 변하면 놀라게 될 것입니다.

이 인수를 논리적 인 극단으로 가져 가면 모든 메서드를 만들어야합니다. final그렇지 않으면 하위 클래스가 수퍼 클래스의 계약을 준수하지 않는 방식으로 메서드를 재정의 할 수 있기 때문입니다. 대부분의 자바 프로그래머가 이것을 우스꽝스럽게 여기는 것은 흥미롭지 만, 불변 클래스는 final. 나는 그것이 일반적으로 Java 프로그래머가 불변성 개념에 완전히 익숙하지 않은 것과 관련이 있다고 생각 final하며 Java 에서 키워드 의 여러 의미와 관련된 일종의 모호한 사고와 관련이 있다고 생각 합니다.

수퍼 클래스의 계약을 준수하는 것은 컴파일러가 항상 강제 할 수있는 것이 아닙니다. 컴파일러는 계약의 특정 측면 (예 : 최소한의 메서드 및 해당 유형 서명 집합)을 적용 할 수 있지만 일반적인 계약에는 컴파일러가 적용 할 수없는 많은 부분이 있습니다.

불변성은 클래스 계약의 일부입니다. 대부분의 Java (일반적으로 OOP) 프로그래머는 계약을 다음과 관련하여 생각하는 경향이있는 반면, 클래스 (및 모든 하위 클래스)가 할 수없는 것을 말하고 있기 때문에 사람들이 더 익숙한 것 중 일부와는 약간 다릅니다. 수업이 할 수 있는 것이 아니라 할 수있는 것.

불변성은 또한 단일 메소드 이상에 영향을 미칩니다. 전체 인스턴스에 영향을 미칩니다. 그러나 이것은 Java 작업 방식 equals과 크게 다르지 않습니다 hashCode. 이 두 가지 방법에는 Object. 이 계약은 이러한 방법으로 할 수없는 일을 매우 신중하게 설명 합니다. 이 계약은 하위 클래스에서 더 구체적으로 만들어집니다. 계약 을 무시 equals하거나 hashCode위반하는 것은 매우 쉽습니다 . 실제로이 두 방법 중 하나만 다른 방법없이 재정의하면 계약을 위반할 가능성이 있습니다. 그래서해야 equals하고 hashCode선언 된 final에 .Object 이 문제를 방지하려면? 나는 대부분의 사람들이 그렇게해서는 안된다고 주장 할 것이라고 생각합니다. 마찬가지로 불변 클래스를 만들 필요는 없습니다.final

말했다 즉, 불변 여부 수업의 대부분은 아마 해야final. 효과적인 Java Second Edition 항목 17 : “상속을위한 설계 및 문서화”를 참조하십시오 .

따라서 3 단계의 올바른 버전은 다음과 같습니다. “클래스를 최종 클래스로 만들거나, 서브 클래스를 위해 디자인 할 때 모든 서브 클래스가 계속 변경 불가능해야한다는 것을 명확하게 문서화하십시오.”


답변

전체 수업을 최종으로 표시하지 마십시오.

다른 답변 중 일부에 명시된 것처럼 불변 클래스를 확장하도록 허용하는 유효한 이유가 있으므로 클래스를 최종 클래스로 표시하는 것이 항상 좋은 생각은 아닙니다.

속성을 비공개 및 최종으로 표시하고 “계약”을 보호하려면 게터를 최종으로 표시하는 것이 좋습니다.

이런 식으로 클래스를 확장 할 수 있지만 (예, 변경 가능한 클래스에 의해서도 가능) 클래스의 변경 불가능한 측면은 보호됩니다. 속성은 비공개이며 액세스 할 수 없습니다. 이러한 속성에 대한 getter는 최종 속성이며 재정의 할 수 없습니다.

불변 클래스의 인스턴스를 사용하는 다른 코드는 전달되는 하위 클래스가 다른 측면에서 변경 가능하더라도 클래스의 불변 측면에 의존 할 수 있습니다. 물론 클래스의 인스턴스를 사용하기 때문에 이러한 다른 측면에 대해서도 알지 못할 것입니다.


답변

최종적이지 않다면 누구나 클래스를 확장하고 setter를 제공하고, 개인 변수를 섀도 잉하고, 기본적으로 변경 가능하게 만드는 것과 같이 원하는대로 할 수 있습니다.


답변

이는 클래스를 확장하는 다른 클래스를 제한합니다.

최종 클래스는 다른 클래스에 의해 확장 될 수 없습니다.

클래스가 변경 불가능하게 만들고 싶은 클래스를 확장하면 상속 원칙으로 인해 클래스의 상태가 변경 될 수 있습니다.

“변경 될 수 있음”을 명확히하십시오. 하위 클래스는 메서드 재정의 사용과 같은 수퍼 클래스 동작을 재정의 할 수 있습니다 (예 : templatetypedef / Ted Hop 답변).


답변

최종적으로 만들지 않으면 확장하여 변경 불가능하게 만들 수 있습니다.

public class Immutable {
  privat final int val;
  public Immutable(int val) {
    this.val = val;
  }

  public int getVal() {
    return val;
  }
}

public class FakeImmutable extends Immutable {
  privat int val2;
  public FakeImmutable(int val) {
    super(val);
  }

  public int getVal() {
    return val2;
  }

  public void setVal(int val2) {
    this.val2 = val2;
  }
}

이제 Immutable을 예상하는 모든 클래스에 FakeImmutable을 전달할 수 있으며 예상되는 계약대로 작동하지 않습니다.


답변

변경 불가능한 클래스를 생성하기 위해 클래스를 최종 클래스로 표시 할 필요는 없습니다.

자바 클래스 자체에서 그러한 예 중 하나를 취해 보겠습니다. “BigInteger”클래스는 불변이지만 최종적인 것은 아닙니다.

실제로 불변성은 객체가 생성 한 객체에 따라 수정할 수없는 개념입니다.

JVM 관점에서 생각해 봅시다. JVM 관점에서 모든 스레드는 객체의 동일한 복사본을 공유해야하며 스레드가 액세스하기 전에 완전히 구성되고 구성 후 객체의 상태가 변경되지 않습니다.

불변성은 객체가 생성되면 객체의 상태를 변경할 방법이 없음을 의미하며 이는 컴파일러가 클래스가 불변임을 인식하도록 만드는 세 가지 규칙에 의해 달성되며 다음과 같습니다.

  • 비공개가 아닌 모든 필드는 최종 필드 여야합니다.
  • 객체의 필드를 직접 또는 간접적으로 변경할 수있는 메서드가 클래스에 없는지 확인하십시오.
  • 클래스에 정의 된 개체 참조는 클래스 외부에서 수정할 수 없습니다.

자세한 내용은 아래 URL을 참조하십시오.

http://javaunturnedtopics.blogspot.in/2016/07/can-we-create-immutable-class-without.html