[java] ImmutableMap 또는 Map을 반환하는 것이 더 낫습니까?

Map을 반환해야하는 메서드를 작성하고 있다고 가정 해 보겠습니다 . 예를 들면 :

public Map<String, Integer> foo() {
  return new HashMap<String, Integer>();
}

잠시 생각한 끝에이 맵이 생성되면 수정할 이유가 없다고 판단했습니다. 따라서 ImmutableMap 을 반환하고 싶습니다 .

public Map<String, Integer> foo() {
  return ImmutableMap.of();
}

반환 유형을 일반 맵으로 두어야합니까, 아니면 ImmutableMap을 반환하도록 지정해야합니까?

한쪽에서, 이것이 바로 인터페이스가 만들어진 이유입니다. 구현 세부 정보를 숨 깁니다.

반면에 이렇게두면 다른 개발자들은이 객체가 불변이라는 사실을 놓칠 수 있습니다. 따라서 불변 객체라는 주요 목표를 달성하지 못할 것입니다. 변경할 수있는 개체의 수를 최소화하여 코드를 더 명확하게 만듭니다. 최악의 경우라도 잠시 후 누군가이 객체를 변경하려고 시도 할 수 있으며 이로 인해 런타임 오류가 발생합니다 (컴파일러가 이에 대해 경고하지 않음).



답변

  • 공개용 API를 작성 중이고 불변성이 디자인의 중요한 측면 인 경우 메서드 이름이 반환 된 맵이 불변이라는 것을 명확하게 나타내거나 구체적인 유형을 반환하여 명시 적으로 만들 것입니다. 지도. 제 생각에는 javadoc에서 언급하는 것만으로는 충분하지 않습니다.

    분명히 Guava 구현을 사용하고 있으므로 문서를 살펴 보았고 추상 클래스이므로 실제 구체적인 유형에 약간의 유연성을 제공합니다.

  • 내부 도구 / 라이브러리를 작성하는 경우 일반 Map. 사람들은 자신이 호출하는 코드의 내부에 대해 알거나 최소한 쉽게 액세스 할 수 있습니다.

내 결론은 명시적인 것이 좋다는 것입니다. 일을 우연에 맡기지 마십시오.


답변

ImmutableMap반환 유형으로 가지고 있어야 합니다. Map구현에 의해 지원되지 않는 방법을 포함 ImmutableMap(예 put) 및 표시된 @deprecated하여 ImmutableMap.

더 이상 사용되지 않는 메서드를 사용하면 컴파일러 경고가 발생하며 대부분의 IDE는 사람들이 더 이상 사용되지 않는 메서드를 사용하려고 할 때 경고합니다.

이 고급 경고는 무언가 잘못되었다는 첫 번째 힌트로 런타임 예외를 갖는 것보다 낫습니다.


답변

반면에 이렇게두면 다른 개발자들은이 객체가 불변이라는 사실을 놓칠 수 있습니다.

javadocs에서 언급해야합니다. 개발자들은 그것들을 읽습니다.

따라서 불변 객체라는 주요 목표를 달성하지 못할 것입니다. 변경할 수있는 개체의 수를 최소화하여 코드를 더 명확하게 만듭니다. 최악의 경우라도 잠시 후 누군가이 객체를 변경하려고 시도 할 수 있으며 이로 인해 런타임 오류가 발생합니다 (컴파일러가 이에 대해 경고하지 않음).

테스트되지 않은 코드를 게시하는 개발자는 없습니다. 그리고 그가 그것을 테스트 할 때, 그는 이유뿐만 아니라 그가 불변의 맵에 쓰려고 한 파일과 줄을 볼 때 예외가 발생합니다.

하지만 Map포함 된 객체가 아닌 자체 만 변경할 수 없습니다.


답변

이렇게두면 다른 개발자가이 객체가 불변이라는 사실을 놓칠 수 있습니다.

사실이지만 다른 개발자는 자신의 코드를 테스트하고 적용되는지 확인해야합니다.

그럼에도 불구하고이 문제를 해결할 수있는 두 가지 옵션이 더 있습니다.

  • Javadoc 사용

    @return a immutable map
    
  • 설명이 포함 된 메서드 이름을 선택했습니다.

    public Map<String, Integer> getImmutableMap()
    public Map<String, Integer> getUnmodifiableEntries()
    

    구체적인 사용 사례의 경우 메서드 이름을 더 잘 지정할 수도 있습니다. 예

    public Map<String, Integer> getUnmodifiableCountByWords()
    

너는 어떤 다른 일을 할 수 있니?!

당신은 반환 할 수 있습니다

  • private Map<String, Integer> myMap;
    
    public Map<String, Integer> foo() {
      return new HashMap<String, Integer>(myMap);
    }
    

    이 방법은 많은 클라이언트가 맵을 수정할 것으로 예상하고 맵에 몇 개의 항목 만 포함되어있는 한 사용해야합니다.

  • CopyOnWriteMap

    COW 컬렉션은 일반적으로
    동시성 을 처리해야 할 때 사용됩니다 . 그러나 CopyOnWriteMap은 변경 작업 (예 : 추가, 제거)에서 내부 데이터 구조의 복사본을 생성하기 때문에이 개념은 상황에서도 도움이 될 것입니다.

    이 경우 변경 작업을 제외하고 모든 메서드 호출을 기본 맵에 위임하는 맵 주위에 얇은 래퍼가 필요합니다. 변경 작업이 호출되면 기본 맵의 복사본이 생성되고 모든 추가 호출이이 복사본에 위임됩니다.

    일부 클라이언트가 맵을 수정할 것으로 예상되는 경우이 접근 방식을 사용해야합니다.

    슬프게도 Java에는 그러한 CopyOnWriteMap. 그러나 제 3자를 찾거나 직접 구현할 수 있습니다.

마지막으로지도의 요소는 여전히 변경 가능할 수 있다는 점을 기억해야합니다.


답변

ImmutableMap을 확실히 반환합니다. 이유는 다음과 같습니다.

  • 메서드 서명 (반환 유형 포함)은 자체 문서화되어야합니다. 의견은 고객 서비스와 같습니다. 고객이 의견에 의존해야하는 경우 주요 제품에 결함이있는 것입니다.
  • 어떤 것이 인터페이스인지 클래스인지는 확장하거나 구현할 때만 관련이 있습니다. 인스턴스 (객체)가 주어지면 클라이언트 코드의 99 %는 무언가가 인터페이스인지 클래스인지 알거나 신경 쓰지 않습니다. 처음에는 ImmutableMap이 인터페이스라고 가정했습니다. 링크를 클릭 한 후에야 그것이 수업이라는 것을 깨달았습니다.


답변

수업 자체에 따라 다릅니다. 구아바 ImmutableMap는 변경 가능한 클래스에 대한 변경 불가능한보기를 의도하지 않습니다. 클래스가 불변하고 기본적으로 인 구조가 있다면 ImmutableMap반환 유형을 만드십시오 ImmutableMap. 그러나 클래스가 변경 가능하면하지 마십시오. 이 경우 :

public ImmutableMap<String, Integer> foo() {
    return ImmutableMap.copyOf(internalMap);
}

Guava는 매번지도를 복사합니다. 느립니다. 그러나 internalMap이미 이면 ImmutableMap완전히 괜찮습니다.

클래스를 returning으로 제한하지 않으면 ImmutableMap대신 다음 Collections.unmodifiableMap과 같이 반환 할 수 있습니다 .

public Map<String, Integer> foo() {
    return Collections.unmodifiableMap(internalMap);
}

이것은 지도에 대한 변경 불가능한 보기 입니다. 경우 internalMap변경, 그래서의 복사 캐시합니다 Collections.unmodifiableMap(internalMap). 그러나 나는 여전히 게터에게 그것을 선호합니다.


답변

정확한 질문에 대한 답은 아니지만지도를 반환해야하는지 여부를 고려해 볼 가치가 있습니다. 맵이 변경 불가능한 경우 제공되는 기본 메소드는 get (key)를 기반으로합니다.

public Integer fooOf(String key) {
    return map.get(key);
}

이것은 API를 훨씬 더 단단하게 만듭니다. 맵이 실제로 필요한 경우 항목 스트림을 제공하여 API 클라이언트에 맡길 수 있습니다.

public Stream<Map.Entry<String, Integer>> foos() {
    map.entrySet().stream()
}

그런 다음 클라이언트는 필요에 따라 자체 불변 또는 변경 가능한 맵을 만들거나 항목을 자체 맵에 추가 할 수 있습니다. 클라이언트가 값이 존재하는지 알아야하는 경우 대신 선택 사항이 리턴 될 수 있습니다.

public Optional<Integer> fooOf(String key) {
    return Optional.ofNullable(map.get(key));
}