포스트

2021-02-24-TIL

“Read-only” Instance - Collections.unmodifiableList()

1
2
3
4
5
class ClassA {
		public void callee (List<String> copiedList) {
				copiedList.remove(0); // This must be banned!
		}
}
1
2
3
4
5
6
7
8
9
10
class ClassB {
		public void caller () {
				ClassA a = new ClassA();
				List<String> originalList = new ArrayList<>();
				originalList.add("java");
				originalList.add("Collections");
				originalList.add("unmodifiableList");
				a.callee(originalList);
		}
}

자바에서 객체를 리턴해주면 얕은 복사(shallow copy)가 되어서 넘어간다. 즉, List<String> copiedList 변수에 ArrayList<String> originalList 객체를 넘겨주면 객체의 주소가 직접 복사되어 넘어간다. 따라서 참조 변수를 통해 remove 등 배열의 요소에 접근하여 수정을 할 수 있는 문제가 발생한다. 따라서 읽기 전용으로 사용하고자 할 때, 외부클래스에서 수정을 못하도록 Collections.unmodifiableList를 사용하여 막을 수 있다.

참고로, final로 선언해도 그 참조값만 바꿀 수가 없다는 것이지 List의 각 요소는 얼마든지 바뀔 수가 있다. 그렇기 때문에 불변객체로 선언하여 각 요소에 대한 수정까지 완전히 막아주는 것이 바람직하다.

Collections.unmodifiableList()가 돌려주는 것은 복사본이 아니라 원본을 감싼 “수정 불가능한 뷰(unmodifiable view)”다. Javadoc에 따르면 조회는 원본 리스트를 그대로 읽고, 반환된 리스트를 통해 수정하려 하면 UnsupportedOperationException이 난다. 그래서 callee가 remove(0)을 호출하는 것은 막을 수 있지만, 원본 참조를 가진 쪽이 원본을 바꾸면 그 변경은 뷰에도 그대로 보인다.

원본과의 연결까지 끊고 싶다면 Java 10에 추가된 List.copyOf()를 쓴다. List.copyOf()는 주어진 컬렉션의 원소를 담은 수정 불가능한 리스트를 만들고, 이후 원본이 바뀌어도 그 변경이 반영되지 않는다. 다만 두 방법 모두 리스트 구조만 막을 뿐이라, 원소 자체가 가변 객체라면 원소의 내용은 바뀔 수 있다.

1
2
List<String> view = Collections.unmodifiableList(originalList); // 원본 변경이 보인다
List<String> copy = List.copyOf(originalList);                  // 원본 변경이 보이지 않는다

[CODE REVIEW] Mission 4: 모든 기물 배치하기 by Rodin

Board

  • chess 패키지 안에 pieces가 있는데 요구사항과는 다릅니다. chess 패키지로 나눈 이유가 따로 있는 것인지는 잘 모르겠지만, 두 개의 패키지를 병렬 배치하는 것이 좋을 것 같습니다.
  • emptyLine() 메서드가 반드시 필요한 것인지, 그냥 필드로 선언해도 되는지 고민해볼 필요가 있는 것 같습니다. (담당하는 기능이 거의 없는 것 같습니다.)
  • pieceCount()는 getter와 역할이 완전히 동일한데, getPieceCount()로 선언하는 것도 그 의미를 더 명확히 하기 위해서 나쁘진 않을 것 같습니다. (하지만 크게 상관 x)
  • 오류를 내지 않고 검증이 된 piece만 추가하는 방법이 있습니다.

GameMain

  • try-with-resources 문 안에 ;는 여러 자원을 서술하지 않는 경우에는 필요가 없습니다.
  • start, end 이외의 명령에 대해서 처리를 해주는 것이 좀 더 좋을 것 같습니다.

StringUtils

  • EOF를 추가하는 것이 좋다고 합니다.

BoardTest

  • assertEquals(expected, actual) -> assertThat(actual).isEqualTo(expected)

다른 리뷰어들의 의견

[roach]

  • method로 빼서 color나 piece를 검증하는 로직을 추가하면 될 것 같다. 하지만 나중에 자료구조가 바뀌므로 그때 변경 가능한 부분
  • initialize()는 메서드 분리를 해도 좋을 것 같다.
  • getWhitePawnResult 시리즈는 중복인 것 같다.
  • emptyLine()은 굳이 메서드로 관리될 필요가 없다.
  • 필드에 final 처리가 잘 안 되어있다. 변경이 안 되도록 막아줄 필요가 있다.
  • board.initialize()를 @BeforeEach로

[Wan]

  • by Honux -> getBlackPawnsResult() 이름이 직관적이지 않다는 지적
  • blackResult 지역변수와 이름이 겹친다? -> 괜찮다
  • pieceCount 테스트 코드에 값->갯수 DisplayName을 정확하게 작성하는 것이 좋다.

[Freddy]

  • try-with-resources에서 세미콜론 생략
  • catch문은 예외에 대한 동작을 이어나가고 싶을 때 사용한다. 지금은 크게 필요 없어 보인다.
  • 패키지 변경 요망
  • pieceCount는 메서드 내부에서 계속 카운트하지 말고, list의 사이즈를 가져오는 것이 의미상이나 논리상으로나 좀 더 정확한 것 같다.
  • Piece에서 color를 검증하는 것이 옳다. color가 red나 blue가 들어오면? Piece 생성자에서 검증을 한다던지.. representation도 마찬가지인데, 일일이 검증하기 복잡해지므로 다른 구조로 설계하는 것이 나을 것이라는 생각이 들 것이다.
  • 8개까지만 추가하고 더 이상 추가하지 못하도록 예외처리 할 수도 있다.
  • 초기화를 안 한 상태에서 이전의 객체를 그대로 쓰면 테스트가 의도와 다르게 작동할 수 있다.

[hiro]

  • enum을 사용하면 Freddy가 지적한 부분이 해결될 수 있을 것 -> hiro의 PR 참조
  • enum은 소문자/대문자 각각 따로 두었다가 지적받음. 아예 따로 클래스로 빼는 게 좋다고 함.

[Woody]

  • 지역변수를 밖으로 빼는 것도 좋은 것 같다.
  • 정렬요정

[Tree]

  • 테스트코드를 메서드 별로 작성한 게 좋아 보인다.
  • enum은 equals 말고 ==로 비교 가능해서 좀 더 좋은 것 같다.

보일러 플레이트 - 핵심 로직을 제외한 코드

리뷰에서 나온 문법 확인

세미콜론 지적은 Java 언어 명세(JLS 14.20.3)의 문법과 맞는다. try-with-resources의 자원 목록은 ( ResourceList [;] ) 형태라서 마지막 자원 뒤의 세미콜론은 넣어도 되고 빼도 된다. 자원이 여러 개일 때 사이를 구분하는 용도로만 반드시 필요하다.

enum을 ==로 비교하는 것도 명세가 허용한다. JLS 8.9는 enum 상수마다 인스턴스가 하나뿐이므로, 두 참조 중 하나가 enum 상수인 것을 알면 equals 대신 ==를 써도 된다고 설명한다.


알고리즘 스터디

String “ “ + “ “ + “ “

공백이 섞인 문자열을 다루는 문제에서 정규 표현식을 찾아봤다. Java에서는 java.util.regex의 Pattern과 Matcher를 쓰고, String.split()이나 replaceAll()에도 정규식을 넘길 수 있다. \s는 공백 문자([ \t\n\r\f]), +는 앞의 표현식이 1회 이상 일치한다는 뜻이므로, 공백이 몇 개든 하나의 구분자로 보려면 \\s+를 쓴다.

1
String[] words = "a  b   c".split("\\s+"); // [a, b, c]

study-whiteship

안녕하세요? 백기선님의 라이브 스터디를 뒤늦게나마 따라가기 위해 스터디 그룹을 만들었습니다. 다들 의견을 수렴하여 스터디 방향을 잡고 시작하려고 합니다.

  1. 목표: 백기선님의 라이브 스터디(18주 과정) 완료

  2. 시간: 매주 토요일 10:00 ~ 12:00 (약 2시간)

  3. 스터디 진행 방식

    • TODO: 스터디 진행 시간표

    • TODO: 의논해보고 결정

  4. 스터디 유의사항

    • 2주차의 진도를 한 주 만에 끝내는 것이 목표이므로 다소 빡셀(?) 수 있습니다. 하지만 ‘꼼꼼히 다 한다’보다는 ‘한번이라도 모든 범위를 다루는 것’이 목표입니다.
  5. 참여인원

    • 적정인원은 4~5명 정도라는 의견이 있습니다. 이 부분은 좀 더 이야기 해보고 결정하면 될 것 같습니다.
    • 참여 확정인원: 4명 (Dong, August, roach, 새리)
참고한 자료외부 출처 7

외부 출처

이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다