2012-10-18 4 views
3

"원본 데이터에 대한 변경 불가능한 인터페이스를 반환하면 개체의 필드를 변경할 수 있지만 호출자는 전송을 통해 속임수를 쓰지 않으면 호출자가 사용자가 원하는 메서드 만 표시합니다. 클래스와 동일하게 처리하는 것은 더 까다 롭습니다. 서브 클래스가 그것의 슈퍼 클래스가하는 모든 것을 드러내야하기 때문에 "이것은 Java 불변의 결과를 돌려주는 것에 대해 무엇을 의미합니까?

당신이 속일 수 있다는 것은 무엇을 의미하며, 서브 클래스에서는 왜 까다로운가?

소스 : http://mindprod.com/jgloss/immutable.html

답변

8

당신은 어떤 돌연변이 방법이없는 interface을 제공합니다. 그런 다음 작성자에게만 알려진 변경 가능한 구현을 제공합니다. 당신이 사방 Person 개체를 반환하는 경우이 시점에서

public interface Person 
{ 
    String getName(); 
} 

public class MutablePerson implements Person 
{ 
    private String name; 

    public MutablePerson(String name) 
    { 
     this.name = name; 
    } 

    @Override 
    public String getName() 
    { 
     return name; 
    } 

    public void setName(String name) 
    { 
     this.name = name; 
    } 
} 

는, 누군가가 반환 된 객체를 수정하는 유일한 방법은 속임수와 MutablePerson로 다시 캐스팅하는 것입니다. 실제로 코드가 완전한 해킹이 아니면 변경 가능한 객체는 변경되지 않습니다.

Person person = new MutablePerson("picky"); 
// someone is cheating: 
MutablePerson mutableAgain = (MutablePerson)person; 
mutableAgain.setName("Phoenix"); 

// person.getName().equals("Phoenix") == true 

실제 구현이 변경 가능한주의 할 것이다, 따라서 그들은 그것을 변경 캐스트 할 수 있습니다 젊은 프로그래머의 무리와 함께 처리하지 않을 경우, 당신은 넣을 수 있다는 장점과 불변성의 안전을 제공 함께 무한한 생성자가 없거나 빌더를 사용합니다 (실제로 변경 가능한 버전은 빌더입니다). 변경 가능한 버전을 악용하는 개발자를 피하는 좋은 방법은 변경 가능 버전을 패키지 전용으로 두어 패키지 만 알 수 있도록하는 것입니다. 이 아이디어의 부정은 이것이 동일한 패키지에서 인스턴스화 될 경우에만 작동한다는 것입니다. 그러나 DAO가 여러 패키지 정의 구현 (예 : , MySQL, Oracle, Hibernate, Cassandra 등 모든 패키지가 동일한 패키지를 돌려주고, 패키지가 어수선하게 흩어지는 것을 피하기 위해 서로 분리되어 있기를 바랍니다.

실제 키는 사람들이 추가 인터페이스를 구현하는 것을 제외하고는 Mutable 개체에서 빌드해서는 안됩니다. 확장하고, 불변의 서브 클래스를 돌려주는 경우, 정의에 의해 가변 오브젝트를 공개하면 (자) 불변은 아닙니다. 예를 들면 :

public interface MyType<T> 
{ 
    T getSomething(); 
} 

public class MyTypeImpl<T> implements MyType<T> 
{ 
    private T something; 

    public MyTypeImpl(T something) 
    { 
     this.something = something; 
    } 

    @Override 
    public T getSomething() 
    { 
     return something; 
    } 

    public void setSomething(T something) 
    { 
     this.something = something; 
    } 
} 

public interface MyExtendedType<T> extends MyType<T> 
{ 
    T getMore(); 
} 

public class MyExtendedTypeImpl<T> 
     extends MyTypeImpl<T> 
     implements MyExtendedType<T> 
{ 
    private T more; 

    public MyExtendedTypeImpl(T something, T more) 
    { 
     super(something); 

     this.more = more; 
    } 

    @Override 
    public T getMore() 
    { 
     return more; 
    } 

    public void setMore(T more) 
    { 
     this.more = more; 
    } 
} 

이 자바 Collection의 구현 했어야하는 방식은 정직입니다. 읽기 전용 인터페이스는 Collections.unmodifiable 구현을 대체 할 수 있으므로 예기치 않게 변경 불가능한 객체의 변경 불가능한 버전을 사용하지는 않습니다. 바꾸어 말하면, 당신은 불변성을 결코 숨겨서는 안되지만, 당신은 변경 가능성을 숨길 수 있습니다.

그런 다음 진정으로 수정할 수없는 변경 불가능한 인스턴스를 뿌리면 개발자가 정직하게 유지할 수 있습니다. 나는 그 문이 잘 나가셨 아니라고 생각

public class MyTypeImmutable<T> implements MyType<T> 
{ 
    private final T something; 

    public MyTypeImmutable(T something) 
    { 
     this.something = something; 
    } 

    @Override 
    public T getSomething() 
    { 
     return something; 
    } 
} 
+0

정말 멋진 예제가 가득합니다 !! +1 – Tivie

2

을, 그는 더 다만 변하지보다에 감동 (그리고 사실이야 : 마찬가지로, 나는 가능성 (더 나은 이름으로) 어딘가에 위의 인터페이스의 불변의 버전을 볼 기대 , 그 성명서가 정말로 불변의 것이 아니 었는지).

특정 클래스가 아닌 데이터를 인터페이스로 반환하면 호출자는 인터페이스에서 작업 만 수행해야합니다. 따라서 인터페이스에 getter 메소드 만 있으면 다운 캐스팅없이 데이터를 조작 할 방법이 없어야합니다.

interface AnInterface { 
    void aGetter(); 
} 

class MyMutableClass { 
    void aGetter(); 
    void aSetter(...); 
} 

비록 MyMutableClassAnInterface을 반환하여, 변경 가능한이 계층 구조를 고려, 사용자가 실제로 변경 가능한 객체 알고하지 않습니다. 따라서 객체는 실제로 변경할 수 없지만이를 알기 위해서는 다운 캐스팅 (또는 리플렉션을 사용하여 뮤 테이터 메소드에 액세스해야합니다.)해야합니다. 이 방법에서 MyImmutableSubclass를 반환 할 경우 지금

, (부모 클래스는 불변이 아니기 때문에 정말 아니에요한다)의 당신은 서브 클래스가 "불변"비록

class MyImmutableSubclass extends MyMutableClass { 
    void anotherGetter(); 
} 

했다 가정 해 봅시다, 호출자는 여전히 수 MyMutableClass이 노출하므로 aSetter으로 전화하십시오.

일반적으로 상태를 "누설"하지 않으려면 불변 개체를 사용하는 것이 좋습니다. 진정으로 불변 인 것은 조작이나 의도하지 않은 변경으로부터 안전합니다.

+0

최종 문장에서 어느 정도 암시되었지만 실행 도중에 참조가 변경 될 위험이 없으므로 불변성으로 인해 스레드 안전성이 보장됩니다. 숨겨진 mutability를 통한 불변성은 특정 상황에서 스레드 실행 중에 불변으로 처리된다는 동일한 이점을 제공합니다. 다시 말해, 하나의 스레드가이 스레드를 변경 불가능한 것으로 간주하기 때문에 작성자는 배후에서 수정할 수 없습니다. 이 유형의 변경은 객체를 함께 모으는 편리한 방법입니다. 객체가 함께 변경 될 수 있으면 "잊어 버려야"하므로 반환해야합니다. – pickypg

1

"무언가"로 유형 변환 할 수있는 경우 반환되는 입력란의 유형을 변경할 수 있으므로 속일 수 있습니다. 공용 인터페이스 뒤에 클래스를 숨기고 그 불변 인터페이스를 반환하면 사용자는 인터페이스를 클래스에 입력하여 속임수를 쓸 수 있습니다.

클래스의 비공개 멤버가 하위 클래스에 상속되지 않지만 protected 및 public이 있으므로 하위 클래스는 까다 롭습니다. 즉, 부모 클래스에서 외부에서 액세스 할 수있는 모든 항목을 외부에서 액세스 할 수 있으므로 인터페이스를 사용하는 것처럼 쉽게 사용자를 혼란시킬 수 없습니다. 부모 메소드를 재정의 (override) 할 수 있기 때문에 실제로는 가능 합니다만, 많은 부분이 보이지는 않습니다.

관련 문제