Dev Book Review/Effective Java

[Effective Java] item31. 한정적 와일드 카드를 사용해 API 유연성을 높여라

1. 매개변수화 타입의 불공변 매개변수화 타입은 불공변이다(invariant) : 서로 다른 타입 Type1,Type2가 있을 때 List 은 List 의 하위타입도 아니고 상위타입도 아니다. (리스코프 치환 원칙에 어긋난다.) 이때 불공변 방식보다 유연한 방식 : 한정적 와일드카드 타입 2. 한정적 와일드 카드 타입을 이용한 확장 유연성 극대화를 위해 원소의 생산자나 소비자용 입력 매개변수에 왈일드카드 타입을 사용하라. 입력 매개변수가 생산자와 소비자 역할을 동시에 한다면 와일드 카드 타입을 써도 좋을 게 없다. public void pushAll(Iterable list, int i, int j); 와일드 카드 타입을 사용하면, set, add와 같은 추가하는 메서드를 작성하지 못한다 따라서 실제 타..

[Effective Java] item31. 한정적 와일드 카드를 사용해 API 유연성을 높여라

728x90

1. 매개변수화 타입의 불공변

매개변수화 타입은 불공변이다(invariant) : 서로 다른 타입 Type1,Type2가 있을 때 List<Type1>List<Type2> 의 하위타입도 아니고 상위타입도 아니다. (리스코프 치환 원칙에 어긋난다.)

이때 불공변 방식보다 유연한 방식 : 한정적 와일드카드 타입

 

2. 한정적 와일드 카드 타입을 이용한 확장

유연성 극대화를 위해 원소의 생산자나 소비자용 입력 매개변수에 왈일드카드 타입을 사용하라.

입력 매개변수가 생산자와 소비자 역할을 동시에 한다면 와일드 카드 타입을 써도 좋을 게 없다.

public void pushAll(Iterable<? extends E> src) { // 생산자
    for (E e : src)
    push (e);
}
public void popAll(Collection<? super E> dst) { // 소비자
  dst.addAll(list);
  list.clear();
}

PECS : producer-extends, consumer-super (= Get and Put Princlple)

매개변수화 타입 T가 생산자면 <? extends T>를 사용하고 소비자라면 <? super T>를 사용하라

 

3. 반환타입에서의 한정적 와일드 카드 타입

반환타입에는 한정적 와일드 카드 타입을 사용하면 안된다
유연성을 높여주지 않고 클라이언트 코드에서도 와일드 카드 타입을 사용하게 하기 때문이다.

public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2)

클래스 사용자가 와일드 카드 타입을 신경써야 한다면 그 API에 문제가 있을 가능성이 크다

 

4. 매개 변수와 인수

  • 매개변수(parameter) : 메서드 선언에 정의한 변수 void add(int value)
  • 인수(argument) : 메서드 호출 시 넘기는 '실제 값' add(10)
  • 타입 매개변수(type parameter) : Class Set<T> {...}
  • 타입 인수(type argument) : Set<Integer> = ...

 

5. 예시

public static <E extends Comparable<? super E>> E max(List<? extends E> list)

이렇 게 구현했을 때만 아래 리스트를 max로 처리하는 것이 가능하다.

List<ScheduledFuture<?>> scheduledFulter = ... ;

 

6. 타입매개 변수가 하나이면 와일드카드로 대체하기

메서드 선언에 타입매개변수가 한번만 나오면 와일드 카드로 대체하라

비한정적 타입 매개변수 -> 비한정적 와일드 카드
한정적 타입 매개변수 -> 한정적 와일드 카드

public static <E> void swap(List<E> list, int i, int j);
public static void swap(List<?> list, int i, int j);

와일드 카드 타입을 사용하면, set, add와 같은 추가하는 메서드를 작성하지 못한다 따라서 실제 타입으로 변환해주기 위해 제네릭의 타입 매개변수를 사용해야하는데 이때

와일드 카드 타입을 실제 타입으로 바꿔주는 private 도우미 메서드를 따로 작성한다.
실제 타입을 알아내려면 이 도우미 메서드는 제네릭 메서드여야하기 때문이다.

public class Swap {
    public static void swap(List<?> list, int i, int j) {
        swapHelper(list, i, j);
    }

    // 와일드카드 타입을 실제 타입으로 바꿔주는 private 도우미 메서드
    private static <E> void swapHelper(List<E> list, int i, int j) {
        list.set(i, list.set(j, list.get(i)));
    }
}

외부에서는 와일드 타입 기반의 멋진 선언을 유지할 수 있다.

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item30. 이왕이면 제네릭 메서드로 만들라

1. 제네릭 메서드 만들기 메서드도 제네릭으로 만들 수 있다. ex ) Collections의 '알고리즘' 메서드 메서드 선언에서 원소타입을 타입 매개변수로 지정한다. 메서드 안에서 이 타입 매개변수를 사용하게 수정한다. 타입 매개변수 목록은 메서드의 제한자와 반환타입 사이에 온다. 한정적 와일드 카드 타입을 사용하면, 반환타입 입력타입 등을 좀더 유연하게 개선할 수 있다. public static Set union(Set s1, Set s2){ Set result = new HashSet(s1); result.addAll(s2); return result; } 2. 제네릭 : 불변 객체를 여러 타입으로 활용할 수 있게 만드는 방법 불변객체가 제네릭 타입일 때 여러 타입으로 활용이 가능하다. 요청 타입 ..

[Effective Java] item30. 이왕이면 제네릭 메서드로 만들라

728x90

1. 제네릭 메서드 만들기

메서드도 제네릭으로 만들 수 있다. ex ) Collections의 '알고리즘' 메서드

  • 메서드 선언에서 원소타입을 타입 매개변수로 지정한다.
  • 메서드 안에서 이 타입 매개변수를 사용하게 수정한다.
  • 타입 매개변수 목록은 메서드의 제한자와 반환타입 사이에 온다.
  • 한정적 와일드 카드 타입을 사용하면, 반환타입 입력타입 등을 좀더 유연하게 개선할 수 있다.
public static <E> Set<E> union(Set<E> s1, Set<E> s2){
  Set<E> result = new HashSet<>(s1);
  result.addAll(s2);
  return result;
}

 

2. 제네릭 : 불변 객체를 여러 타입으로 활용할 수 있게 만드는 방법

불변객체가 제네릭 타입일 때 여러 타입으로 활용이 가능하다. 요청 타입 변수에 맞게 객체의 타입을 바꾸어주는 "제네릭 싱글턴 팩터리"가 필요하다.

 

a. 함수객체 : 함수 내부에 들어가는 객체

List<String> str = Arrays.asList("a","b","c");
str.sort(Collectioins.reverseOrder());

제네릭 싱글턴 팩터리 패턴을 띄고있다.

reverseComparator를 형변환하는 코드로 비검사 형변환 경고가 발생하여, @SuppressWarning 애너테이션을 추가하여 경고 없이 컴파일되도록 숨겼다.

 

b. 재귀적 타입 한정 (recursive type bound)

자기 자신이 들어간 표현식을 사용하여 타입 매개변수의 허용범위를 한정한다.

public static <E extends Comparable<E>> E max(Collection<E> c);

모든 타입 E는 자기 자신과 같은 타입인 원소 모두와 비교 가능하다

cf) 시뮬레이트한 셀프타입 관용구 - [아이템 2]

- 관련 자바봄 이슈) 

재귀적 타입 한정을 이용하는 제네릭타입이다.

추상메서드 self 를 지원하여 하위클래스에서 형변환 하지 않고도 메서드 연쇄를 지원할 수 있으며. self 타입이 없는 자바를 위한 우회방법을 이야기 한다.

public abstract class PayCard {
    public enum Benefit {
        POINT("포인트"), SALE("할인"), SUPPORT("연회비지원");
        Benefit(String benefit) {
        }
    }

    final Set<Benefit> benefits;

    abstract static class Builder<T extends Builder<T>> { // 재귀적 타입한정
        EnumSet<Benefit> benefits = EnumSet.noneOf(Benefit.class);

        public T addBenefit(Benefit benefit) {
            this.benefits.add(benefit);
            return self();
        }

        abstract PayCard build();

        protected abstract T self();
    }

    PayCard(Builder<?> builder) {
        benefits = builder.benefits.clone();
    }
}
public static class Builder extends PayCard.Builder<Builder>{
        private final Sale sale;

        public Builder(Sale sale) {
            this.sale = sale;
        }

        @Override
        NaverPayCard build() {
            return new NaverPayCard(this);
        }

        @Override
        protected Builder self() {
            return this;
        }
    }

재귀적 타입 한정을 사용함으로 인해서, PayCard의 하위타입인 NaverPayCard에서 상위 클래스의 메서드를 호출 할 수 있게 된다.

https://github.com/Java-Bom/ReadingRecord/issues/75

 

 

[아이템 30] 시뮬레이트한 셀프 관용구 · Issue #75 · Java-Bom/ReadingRecord

180p 시뮬레이트한 셀프관용구에 대한 설명이 나오는데, 이게 2장의 Builder패턴 pizza를 상속받은 nypizza 빌더패턴에 대한 이야기야, 이거 한번 구현해보고 실제로 왜 재귀적 타입한정을 써야 이런 ��

github.com

 

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item29. 이왕이면 제네릭 타입으로 만들라

1. 제네릭클래스로 만드는 방법 a. 클래스 선언에 타입매개변수를 추가한다. 보통 E를 많이 사용한다. 제네릭 필드를 쓴다는 것을 명시하는 것이다. b. 실체화 불가 타입으로는 배열을 만들 수 없으니 해결한다. E[] element = new E[DEFAULT_INITAL_CAPACITY] b-1. 제네릭 배열을 금지하는 제약을 대놓고 우회한다. E[] elemet = (E[]) new Object[DEFAULT_INITAL_CAPACITY] 비검사 형변환이 프로그램의 타입 안정성을 해치지 않는지를 확인한다. > 이후 @SuppressWarnings 애너테이션으로 해당 경고를 숨긴다. 배열이 private 필드에 저장된다. 클라이언트로 반환되거나 다른 메서드에 전달되는 일이 없다. 배열에 저장되는 원소의..

[Effective Java] item29. 이왕이면 제네릭 타입으로 만들라

728x90

1. 제네릭클래스로 만드는 방법

a. 클래스 선언에 타입매개변수를 추가한다.

  • 보통 E를 많이 사용한다.
  • 제네릭 필드를 쓴다는 것을 명시하는 것이다.

b. 실체화 불가 타입으로는 배열을 만들 수 없으니 해결한다.

E[] element = new E[DEFAULT_INITAL_CAPACITY]

b-1. 제네릭 배열을 금지하는 제약을 대놓고 우회한다.

E[] elemet = (E[]) new Object[DEFAULT_INITAL_CAPACITY]

비검사 형변환이 프로그램의 타입 안정성을 해치지 않는지를 확인한다. > 이후 @SuppressWarnings 애너테이션으로 해당 경고를 숨긴다.

  • 배열이 private 필드에 저장된다.
  • 클라이언트로 반환되거나 다른 메서드에 전달되는 일이 없다.
  • 배열에 저장되는 원소의 타입은 항상 E 이다.

특징

  • 가독성이 더 좋다. : E 타입만을 받는다는 점을 어필한다.
  • 형변환을 배열 생성시 단한번만 해주면된다.
  • 배열의 런타임 타입이 컴파일타임 타입과 달라 힙오염이 일어날 수 있다.

b-2. 배열 필드의 타입을 Object[]로 바꾼다.

E result = (E) elements[--size];

E가 실체화 불가 타입이므로 컴파일러는 런타임에 이뤄지는 형변환이 안전한지 증명할 방법이 없다. > 이후 비검사 형변환을 수행하는 할당문에서만 경고를 숨긴다.

특징

  • 배열에서 원소를 읽을 떄마다 형변환 해주어야 한다.
  • 힙오염이 해가되지 않는다.

 

2. 제네릭 배열을 사용하는 이유

  • 자바가 리스트를 사용하는게 항상 가능하지도, 꼭 더 좋은 것도아니다. 리스트도 결국은 기본타입인 배열을 사용해 구현해야하기 때문이다.
  • 성능향상의 이슈로 배열을 사용할 수 있다.
  • 제네릭타입의 타입매개변수에는 primitive 타입을 사용할 수 없다. 박싱된 기본타입으로 우회할 수 있긴하다.

 

3. 한정적 타입 매개변수

class DelayQueue<E extends Delayed> implements BlockingQueue<E>

Delayed.class의 하위타입만 받는다는 의미이다. (모든 타입은 자기 자신의 하위 타입)

ClassCastException을 걱정할 필요가 없다.

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item28. 배열보다는 리스트를 사용하라

1. 배열과 제네릭의 차이 배열 공변 (convariant) - Sub가 Super의 하위타입이라면 배열 Sub[]는 배열 Super[]의 하위타입이다. (함께 변한다) 배열에서는 실수를 런타임에 타입 오류를 알 수 있다 Object[] objectArray = new Long[1]; objectArray[0] = "타입이 달라 넣을 수 없다" // ArrayStoreException 실체화(reify)된다 : 런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인한다. 제네릭 불공변 (invariant) - 서로 다른 타입 Type1,Type2가 있을 때 List 은 List 의 하위타입도 아니고 상위타입도 아니다 리스트에서는 컴파일할 때 타입 오류를 바로 알 수 있다. List ol = new Ar..

[Effective Java] item28. 배열보다는 리스트를 사용하라

728x90

1. 배열과 제네릭의 차이

배열

  • 공변 (convariant) - SubSuper의 하위타입이라면 배열 Sub[]는 배열 Super[]의 하위타입이다. (함께 변한다)
  • 배열에서는 실수를 런타임에 타입 오류를 알 수 있다
  Object[] objectArray = new Long[1];
  objectArray[0] = "타입이 달라 넣을 수 없다" // ArrayStoreException

실체화(reify)된다 : 런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인한다.

제네릭

  • 불공변 (invariant) - 서로 다른 타입 Type1,Type2가 있을 때 List<Type1>List<Type2> 의 하위타입도 아니고 상위타입도 아니다
  • 리스트에서는 컴파일할 때 타입 오류를 바로 알 수 있다.
  List<Object> ol = new ArrayList<Long>();    // 호환되지 않는 타입이다.
  ol.add("타입이 달라 넣을 수 없다.")

소거(erasure)된다 : 원소 타입을 컴파일 타임에만 검사하며 런타임에는 알 수 없다.
제네릭 지원 전과 제네릭 타입을 함게 사용할 수 있게 해주는 메커니즘이다.

 

2. 제네릭 배열은 사용불가하다.

배열은 제네릭 타입, 매개변수화 타입, 타입 매개변수로 사용할 수 없다.
new List<E>[], new List<String>[], new E[] : 컴파일시 제네릭 생성오류를 일으킨다.

이유 : 타입안전하지 않기 때문이다
컴파일러가 자동 생성한 형변환 코드에서 런타임에 ClassCastException이 발생할 수 있다 : 제네릭 타입의 취지와 맞지 않다.

 

3. 실체화 불가 타입

E, List<E>, List<String> : 실체화 불가 타입

실체화 되지 않아 런타임에는 컴파일 타임보다 타입점보를 적게 가지는 타입

List<?>, Map<?,?> : 소거 메커니즘 때문에 매개변수화 타입 가운데 실체화 가능한 타입 - 비한정적 와일드카드 타입

 

4. 배열의 불편함

a. 제네릭 컬렉션에서는 자신의 원소 타입을 담은 배열을 반환하는게 불가능하다.

아이템 33의 타입 안전 이종 컨테이너를 이용하여 자신의 원소타입을 추론할 수있다 (우회)

b. 제네릭 타입과 가변인수 메서드를 함께 쓰면 해석하기 어려운 경고 메세지를 받게된다.

가변인수 메서드를 호출할 때마다. 가변인수를 담는 배열이 만들어지는데, 이때 그 배열의 원소가 실체화 불가 타입이면 경고가 뜬다. @SafeVarargs로 해결한다.

 

5. 배열대신 리스트를 사용하자

  • 장점 : 타입 안정성과 상호 운용성이 좋아진다.
  • 단점 : 코드가 조금 복잡해지고 성능이 살짝 나빠질 수도 있다.
public class Chooser<T>{
  private final List<T> choiceList;

  public Chooser(Collection<T> choices){
    choiceList = new ArrayList<>(choices);
  }

  public T choose(){
    Random rnd = ThreadLocalRandom.current();
    return choiceList.get(rnd.netInt(choiceList.size()));
  }
}

List를 사용하면 런타임에 ClassCastException을 만날 일은 없다.

public class Chooser<T>{
  private final T[] choiceArray;

  public Chooser(Collection<T> choices){
    choicesArra = (T[]) choices.toArray; 
  }

    public Object choose(){
    Random rnd = ThreadLocalRandom.current();
    return choiceArray[rnd.nextInt(choiceArray.length)];
  }
}

T[] 로의 타입캐스팅 과정에서 경고가 뜬다. 제네릭에서는 원소의 타입정보가 소거되어 런타임에는 타임 정보를 알 수 없기 때문이다. item 27 비검사 경고를 제거하라는 말에 따라 위험요소를 제거할 수 있는 최선의 방법은 List로 구현하는 것이었다.

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item 27. 비검사 경고를 제거하라

1. 할수있는 한 모든 비검사 경고를 제거하자 제네릭을 사용하기 시작했을 때 볼 수 있는 수많은 컴파일러 경고 비검사 형변환 경고 비검사 메서드 호출 경고 비검사 매개변수화 가변인수 타입 경고 비검사 변환 경고 새로 작성한 코드가 한번에 깨끗하게 컴파일되리라 기대하지는 말자 Java7 부터 제네릭 타입추론이 가능해졌다. Set exaltation = new HashSet(); 할 수 있는 한 모든 비검사 경고를 제거하라 : 모두 제거하면 타입 안정성이 보장된다. 런타임에 ClassCastException이 발생할 일이 없고, 내가 의도한대로 잘 동작한다. 2. @SuppressWarning a. 경고를 제거할 수는 없지만 타입 안전하다고 확신할 수 잇을 때 @SuppressWarning으로 경고를 숨기자..

[Effective Java] item 27. 비검사 경고를 제거하라

728x90

1. 할수있는 한 모든 비검사 경고를 제거하자

제네릭을 사용하기 시작했을 때 볼 수 있는 수많은 컴파일러 경고

  • 비검사 형변환 경고
  • 비검사 메서드 호출 경고
  • 비검사 매개변수화 가변인수 타입 경고
  • 비검사 변환 경고

새로 작성한 코드가 한번에 깨끗하게 컴파일되리라 기대하지는 말자

Java7 부터 제네릭 타입추론이 가능해졌다.

Set<Lark> exaltation = new HashSet<>();

할 수 있는 한 모든 비검사 경고를 제거하라 : 모두 제거하면 타입 안정성이 보장된다.
런타임에 ClassCastException이 발생할 일이 없고, 내가 의도한대로 잘 동작한다.

 

2. @SuppressWarning

a. 경고를 제거할 수는 없지만 타입 안전하다고 확신할 수 잇을 때 @SuppressWarning으로 경고를 숨기자

  • 검증 없이 경고 숨길 때 : 경고없이 컴파일이 되어도 런타임에 여전히 ClassCastException을 던질 수 있다.
  • 안전한데 안숨길 때 : 진짜 문제를 알리는 새로운 경고가 나와도 눈치채지 못할 수 있다.

b. 가능한 한 좁은 범위에 적용하자 : 변수 선언, 짧은 메서드, 생성자
범위가 필요이상으로 넓어지면, 지역변수를 하나 선언하고 그 변수에 애너테이션을 달아준다.

c. @SuppressWarning("unchecked") 애너테이션을 사용할 때 그 경고를 무시해도 안전한 이유를 항상 주석으로 남겨야한다.
다른사람이 코드를 이해하는데 도움이 되고, 다른사람이 그 코드를 잘못 수정할 경우를 막는다.

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item26. 로타입은 사용하지 말라

1. 용어정리 public class Example{ private T member; } 제네릭 클래스[인터페이스] : 클래스[인터페이스] 선언에 타입 매개변수(type parameter)가 쓰인다. Example.class 제네릭 타입(Generic Type) : 제네릭 클래스와 제네릭 인터페이스를 통틀어 이르는 말. Example 매개변수화 타입(parameterized Type) : 각각의 제네릭 타입은 parameterizedType을 선언함. Example 타입 매개 변수(Type parameter) : 제네릭 선언에 사용된 매개변수 formalType : Example actualType : Example 로타입(raw type) : 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않았을 때. ..

[Effective Java] item26. 로타입은 사용하지 말라

728x90

1. 용어정리

public class<T> Example{
     private T member; 
}
  • 제네릭 클래스[인터페이스] : 클래스[인터페이스] 선언에 타입 매개변수(type parameter)가 쓰인다. Example.class
  • 제네릭 타입(Generic Type) : 제네릭 클래스와 제네릭 인터페이스를 통틀어 이르는 말. Example<T>
  • 매개변수화 타입(parameterized Type) : 각각의 제네릭 타입은 parameterizedType을 선언함. Example<String>
  • 타입 매개 변수(Type parameter) : 제네릭 선언에 사용된 매개변수 <T>
  • formalType : Example<E>
  • actualType : Example<String>
  • 로타입(raw type) : 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않았을 때. Example
    로타입은 제네릭 전후 코드의 호환을 위한 것이며, 동작하지만 좋은 예는 아니다.

 

2. 제네릭의 타입 안정성

오류는 가능한 한 발생 즉시, 이상적으로는 컴파일 타임에 발견하는 것이 좋다.

a. 로타입의 단점 : 컴파일 타임에 타입 정보를 알지 못한다.

ClassCastException과 같은 런타임에야 알아챌 수 있는 에러를 만들기 쉽다.

실제로 아래 예제는, 로타입의 사용으로 인해 컴파일 타임에는 문제가 없으나, 실제 런타임에는 ClassCastException이 터진다.
Integer값을 String값으로 캐스팅하려 했기 때문이다.

public class Raw {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        unsafeAdd(strings, Integer.valueOf(42));
        String s = strings.get(0); // 컴파일러가 자동으로 형변환 코드를 넣어준다.
    }

    private static void unsafeAdd(List list, Object o) {
        list.add(o);
    }
}
 

b. 제네릭의 장점 : 컴파일 타임에 타입 선언이 녹아든다.

컴파일러가 타입선언에 대해 인지하고 있기 때문에, 아무런 경고 없이 컴파일되면 의도대로 동작할 것임을 보장한다.

public class Raw {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        unsafeAdd(strings, "42");   // 컴파일 타임에 에러를 체크해주어서 바꿀 수 있다.
        String s = strings.get(0);
    }

    private static <T> void unsafeAdd(List<T> list, T o) { // 제네릭 선언
        list.add(o);
    }
}

컴파일러가, 컬렉션에서 원소를 넣는 모든 곳에 보이지 않는 형변환을 추가하여 절대 실패하지 않음을 보장하였다.

++ 추가 내용

 

3. 로타입은 절대로 사용하지 말자.

제네릭이 안겨주는 안정성과 표현력을 모두 잃게된다.

a. 로타입이 만들어진 이유 - 호환성

기존 코드를 모두 수용하면서 제네릭을 사용하는 새로운 코드와 맞물려돌아가게 하기 위해서이다.
호환성 : 로타입 지원 + 제네릭 구현시 소거(erasure) 방식을 이용

List vs List<Object>

  • List : 로타입 - 제네릭타입에서 완전 발은 떼었다.
  • List<Object> : 임의 객체를 허용하는 매개변수화 타입 - 타입에 대한 정보를 컴파일러에 알려주었다.
  • 제네릭의 하위타입 규칙 : List<String>은 로타입인 List의 하위타입이지만, List<Object>의 하위타입은 아니다.

로타입을 사용하면 타입 안정성을 잃게된다.

 

4. 타입 안전 + 유연 : 비한정 와일드카드 타입

비한정 와일드카드 타입(unbounded wildcard type) : 제네릭 타입을 쓰고 싶지만, 실제 타입 매개변수가 무엇인지 신경쓰고 싶지 않을 때 사용한다. Set<E>의 비한정 와일드카드 타입은 Set<?>

static int numElementsInCommon(Set<?> s1, Set<?> s2){...}

와일드카드 타입은 안전하고 로타입은 안전하지 않다.

  • 로타입 : 아무 원소나 넣을 수 있어 타입 불변식을 훼손하지 쉽다.
  • 와일드카드 타입 : Collection<?>에는 어떤 원소도 넣을 수 없다. (null 외에는)

    컴파일 타임에 오류메세지를 볼 수 있다.
    컬렉션의 타입 불변식을 훼손하지 못하게 막고, 컬렉션에서 꺼낼 수 있는 객체의 타입도 알수 없게 한다.

 

5. 로타입의 규칙예외

a. class 리터럴에는 로타입을 사용한다

자바 명세 자체가 class 리터럴에 매개변수화 타입을 사용하지 못하게 하였다.

  • 허용 : List.class, String[].class, int.class
  • 불가 : List<String>.class, List<?>.class

b. Instanceof 연산자는 로타입을 사용한다.

instanceof 연산자는 비한정 와일드카드 타입 이외의 매개변수화 타입에는 적용할 수 없다.

로타입이든 비한정적 와일드카드 타입이든 instanceof는 똑같이 동작한다. : <?> 코드만 지저분해지므로 로타입

런타임에는 제네릭 타입정보가 지워진다.

if(o instanceof Set){            // 로타입
  Set<?> s = (Set<?>) o;    // 와일드카드 타입
}

 

6. 용어정리

한글 영문
매개변수화 타입 parameterized type List<String>
실제 타입 매개변수 actual type parameter String
제네릭 타입 generic type List<E>
정규 타입 매개변수 formal type parameter E
비한정적 와일드카드 타입 unbounded wildcard type List<?>
로 타입 raw type List
한정적 타입 매개변수 bounded type parameter <E extends Number>
재귀적 타입 한정 recursive type bound <T extends Comparable<T>>
한정적 와일드카드 타입 Bounded wildcard type <? extends Number>
제네릭 메서드 generic method static <E> List<E> asList(E[] a)
타입 토큰 type token String.class

댓글

Comments

Develop/git-github

git status 한글 깨짐 | git status Korean character broken

git status를 할 때, 한글이름을 가지는 파일일 경우에 /200/300/385 이런식으로 파일명이 깨지는 경우가 있다. (mac 터미널)git config --global core.quotepath false위 설정으로 바꾸면 올바르게 한글이름 파일을 git status로 상태확인이 가능해진다.core.quotePathCommands that output paths (e.g. ls-files, diff), will quote "unusual" characters in the pathname by enclosing the pathname in double-quotes and escaping those characters with backslashes in the same way C escapes c..

git status 한글 깨짐 | git status Korean character broken

728x90

git status를 할 때, 한글이름을 가지는 파일일 경우에 /200/300/385 이런식으로 파일명이 깨지는 경우가 있다. (mac 터미널)

git config --global core.quotepath false

위 설정으로 바꾸면 올바르게 한글이름 파일을 git status로 상태확인이 가능해진다.

core.quotePath

Commands that output paths (e.g. ls-files, diff), will quote "unusual" characters in the pathname by enclosing the pathname in double-quotes and escaping those characters with backslashes in the same way C escapes control characters (e.g. \t for TAB, \n for LF, \\ for backslash) or bytes with values larger than 0x80 (e.g. octal \302\265 for "micro" in UTF-8). If this variable is set to false, bytes higher than 0x80 are not considered "unusual" any more. Double-quotes, backslash and control characters are always escaped regardless of the setting of this variable. A simple space character is not considered "unusual". Many commands can output pathnames completely verbatim using the -z option. The default value is true.

output path에 대한 커맨드는 unusual인 패스 이름을 조정한다. ( " 가 들어가 있거나, escaping 이 들어가 있는 경우 )
이때 한글 인코딩이 UTF-8에 들어가 0x80 보다 큰 바이트를 가진 escape 문자 처리가 되어 "unusual"인 케이스로 포함이 된다.

그래서 이 변수를 false로 설정하면

0x80보다 높은 바이트는 더 이상 "unusual" 인 것으로 간주되지 않는다.
"unusual"로 간주되는 큰 따옴표, 백 슬래시 및 제어 문자는 이 변수의 설정에 관계없이 항상 이스케이프 되며,
단순한 공백 문자는 "unusual"로 간주되지 않는다.

참고 : https://git-scm.com/docs/git-config#Documentation/git-config.txt-corequotePath

 

Git - git-config Documentation

When using the deprecated [section.subsection] syntax, changing a value will result in adding a multi-line key instead of a change, if the subsection is given with at least one uppercase character. For example when the config looks like [section.subsection

git-scm.com

 

When running git status, if a file has a Korean name, the filename may appear garbled like /200/300/385. (Mac terminal)

git config --global core.quotepath false

If you change to the above setting, you'll be able to correctly check the status of files with Korean names using git status.

core.quotePath

Commands that output paths (e.g. ls-files, diff), will quote "unusual" characters in the pathname by enclosing the pathname in double-quotes and escaping those characters with backslashes in the same way C escapes control characters (e.g. \t for TAB, \n for LF, \\ for backslash) or bytes with values larger than 0x80 (e.g. octal \302\265 for "micro" in UTF-8). If this variable is set to false, bytes higher than 0x80 are not considered "unusual" any more. Double-quotes, backslash and control characters are always escaped regardless of the setting of this variable. A simple space character is not considered "unusual". Many commands can output pathnames completely verbatim using the -z option. The default value is true.

Commands that deal with output paths adjust pathnames that are considered unusual. (e.g., when they contain double quotes or escape characters)
In this case, Korean characters encoded in UTF-8 have bytes larger than 0x80, so they get treated as escape characters and fall under the "unusual" case.

So if you set this variable to false:

Bytes higher than 0x80 are no longer considered "unusual".
Double-quotes, backslashes, and control characters that are considered "unusual" are always escaped regardless of this variable's setting,
and simple space characters are not considered "unusual".

Reference: https://git-scm.com/docs/git-config#Documentation/git-config.txt-corequotePath

 

Git - git-config Documentation

When using the deprecated [section.subsection] syntax, changing a value will result in adding a multi-line key instead of a change, if the subsection is given with at least one uppercase character. For example when the config looks like [section.subsection

git-scm.com

 

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] Chapter 4: 클래스와 인터페이스

item15. 클래스와 멤버의 접근 권한을 최소화 하라 프로그램 요소의 접근성은 가능한 한 최소한으로 하라. 꼭 필요한 것만 골라 최소한의 public API를 설계하자. 그 외에는 클래스, 인터페이스, 멤버가 의도치 않게 API로 공개되는 일이 없도록 해야한다. public 클래스는 상수용 public static final 필드 외에는 어떠한 public 필드를 가져서는 안 된다. public static final 필드가 참조하는 객체가 불변인지 확인하라. Link : https://jyami.tistory.com/77 [Effective Java] item 15. 클래스와 멤버의 접근 권한을 최소화하라 잘 설계된 컴포넌트 : 클래스 내부 데이터와 내부 구현정보를 외부 컴포넌트로부터 얼마나 잘 숨겼..

[Effective Java] Chapter 4: 클래스와 인터페이스

728x90

item15. 클래스와 멤버의 접근 권한을 최소화 하라

  • 프로그램 요소의 접근성은 가능한 한 최소한으로 하라.
  • 꼭 필요한 것만 골라 최소한의 public API를 설계하자.
  • 그 외에는 클래스, 인터페이스, 멤버가 의도치 않게 API로 공개되는 일이 없도록 해야한다.
  • public 클래스는 상수용 public static final 필드 외에는 어떠한 public 필드를 가져서는 안 된다.
  • public static final 필드가 참조하는 객체가 불변인지 확인하라.
  • Link : https://jyami.tistory.com/77
 

[Effective Java] item 15. 클래스와 멤버의 접근 권한을 최소화하라

잘 설계된 컴포넌트 : 클래스 내부 데이터와 내부 구현정보를 외부 컴포넌트로부터 얼마나 잘 숨겼는가 오직 API를 통해서만 다른 컴포넌트와 소통한다. [정보은닉, 캡슐화] 1. 정보 은닉의 장점 시스템 개발 속도..

jyami.tistory.com

 

item16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라

  • public 클래스는 절대 가변 필드를 직접 노출해서는 안 된다.
  • 불변 필드라면 노출해도 덜 위험하지만 완전히 안심할 수는 없다.
  • 하지만 Package-private 클래스나 private 중첩 클래스에서는 종종(불변이든 가변이든) 필드를 노출하는 편이 나을 때도 있다.
  • Link : https://jyami.tistory.com/78
 

[Effective Java] Item16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라

1. public 클래스의 가변 필드 절대 가변 필드를 public으로 노출하면 안된다. 캡슐화의 이점을 제공하지 못한다. API를 수정하지 않고는 내부 표현을 바꿀 수 없다. 불변식을 보장할 수 없다. 외부에서 필드에 접..

jyami.tistory.com

 

item17. 변경 가능성을 최소화하라

  • 클래스가 꼭 필요한 경우가 아니면 불변이어야 한다. (getter가 있다고 해서 setter를 무조껀 만들지는 말자)
  • 무거운 값객체의 경우 성능때문에 어쩔수 없다면, 불변 클래스와 쌍을 이루는 가변 동반 클래스를 public 클래스로 제공하자.
  • 불변으로 만들 수 없는 클래스라도 변경할 수 있는 부분을 최소한으로 줄이자
  • 다른 합당한 이유가 없다면 모든 필드는 private final이어야 한다.
  • 생성자는 불변식 설정이 모두 완료된, 초기화가 완벽히 끝난 상태의 객체를 생성해야한다.
  • Link : https://jyami.tistory.com/79
 

[Effective Java] item17. 변경가능성을 최소화하라

불변 클래스 : 그 인스턴스의 내부 값을 수정할 수 없는 클래스 예 ) String, Wrapper Class, BigInteger, BigDecimal - 설계 구현 사용이 쉽다. 오류 여지가 적고 안전하다. 1. 불변 클래스 5가지 규칙 ㄱ. 객체..

jyami.tistory.com

 

item18. 상속보다는 컴포지션을 사용하라

  • 상속은 강력하지만 캡슐화를 깨진다.
  • 상속은 상위 클래스와 하위 클래스가 순수한 is-a 관계일 때만 사용해야한다.
  • is-a 관계여도 안심할 수만은 없다 : 하위 클래스의 패키지가 상위클래스와 다르고, 상위클래스가 확장을 위해 설계되지 않았다면 문제가 될 수 있다.
  • 상속의 취약점을 피하려면 상속대신 컴포지션과 전달을 사용하자
  • 래퍼 클래스로 구현할 적당한 인터페이스가 있으면 컴포지션을 특히 더 사용하자
  • 래퍼클래스는 해위 클래스보다 견고하고 강력하다.
  • Link : https://jyami.tistory.com/80
 

[Effective Java] Item18. 상속보다는 컴포지션을 사용하라

상속이 안전 할 때 상위 클래스와 하위클래스를 모두 같은 프로그래머가 통제하는 패키지 안에서 사용한다. 확장할 목적으로 설계되었고 문서화도 잘되었다. 상속이 안전하지 않을때 다른 패키

jyami.tistory.com

 

item19. 상속을 고려해 설계하고 문서화하라. 그렇지 않았다면 상속을 금지하라

  • 상속용 클래스를 설계하는건 만만치 않다.
  • 클래스 내부에서 스스로를 어떻게 사용하는지 (자기 사용패턴) 모두 문서로 남겨야한다.
  • 문서화 한 것은 그 클래스가 쓰이는 한 반드시 지킨다.
  • 지키지 않으면 그 내부 구현 방식을 믿고 활용하던 하위클래스를 오동작하게 만들 수 있다.
  • 다른 이가 효율좋은 하위 클래스를 만들 수 있도록 일부 메서드를 protected로 제공할 수도 있다.
  • 클래스를 확장해야 할 명확한 이유가 떠오르지 않으면 상속을 금지하자
  • 상속을 금지하려면 클래스를 final로 선언하거나, 생성자 모두를 외부에서 접근할 수 없게 만들면 된다.
  • Link : https://jyami.tistory.com/81
 

[Effective Java] item 19. 상속을 고려해 설계하고 문서화하라. 그렇지 않았다면 상속을 금지하라

1. 재정의 메서드의 문서화 상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지(자기사용) 문서로 남겨야한다. 재정의 가능 메서드 : 호출 메서드의 API 설명에 기술, 호출 순서, 각각의..

jyami.tistory.com

 

item20. 추상 클래스 보다는 인터페이스를 우선하라

  • 다중 구현용 타입으로 인터페이스가 적합하다.
  • 복잡한 인터페이스라면 구현하는 수고를 덜어주는 골격 구현을 함께 제공하는 방법을 고려해보자
  • 골격 구현은 '가능한 한' 인터페이스의 디폴트 메서드로 제공하여 그 인터페이스를 구현한 모든 곳에서 활용하도록 하는 것이 좋다.
  • '가능한 한'의 이유는, 인터페이스에 걸려잇는 구현상의 제약 때문에 골격 구현을 추상 클래스로 제공하는 경우가 더 흔하기 때문이다.
  • Link : https://jyami.tistory.com/82
 

[Effective Java] Item20. 추상 클래스 보다는 인터페이스를 우선하라

자바의 다중 구현 메커니즘 : 둘다 인스턴스 메서드를 구현 형태로 제공할 수 있다 (default method) 인터페이스 : 다중 상속, 같은 타입 취급 추상클래스 : 단일 상속, 하위 클래스 (상하 관계) 1. 인터

jyami.tistory.com

 

item21. 인터페이스는 구현하는 쪽을 생각해 설계하라

  • 디폴트 메서드는 구현 클래스에 대해 아무것도 모른채 합의 없이 무작정 '삽입'될 뿐이다
  • 범용적으로 구현하겠지만, 모든 구현체와 어울리는 것은 아니기 때문이다.
  • 꼭 필요한 경우가 아니라면 디폴트메서드를 기존인터페이스에서 추가하는건 금하자.
  • 새 인터페이스 만들때는 유용하다
  • 인터페이스 설계는 세심한 주의를 기울이자 : 3가지 구현체 만들어보기
  • Link : https://jyami.tistory.com/83
 

[Effective Java] item21. 인터페이스는 구현하는 쪽을 생각해 설계하라

생각할 수 있는 상황에서 불변식을 해치지 않는 디폴트 메서드 작성은 어렵다. 디폴트 메서드는 구현 클래스에 대해 아무 것도 모른채 합의 없이 무작정 '삽입'될 뿐이다. Java8 : 컬렉션 인터페이

jyami.tistory.com

 

item22. 인터페이스는 타입을 정의하는 용도로만 사용하라

  • 인터페이스는 타입을 정의하는 용도로만 사용해야한다. 상수 공개용 수단으로 사용하지 말자ㄴ
  • 상수 인터페이스는 만들지 말자
  • 상수 공개를 원하면, 연관 클래스, 이넘, 인스턴스화 불가 유틸 클래스를 이용하자
  • Link : https://jyami.tistory.com/84
 

[Effective Java] item22. 인터페이스는 타입을 정의하는 용도로만 사용하라

인터페이스의 용도 : 자신의 인스턴스로 무엇을 할 수 있는지를 클라이언트에 얘기해준다. 상수인터페이스는 만들지 말자 외부 인터페이스가아닌 내부구현에 해당하며 클래스의 API로 노출하는 행위이다. 상수를..

jyami.tistory.com

 

item23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라

  • 태그달린 클래스를 써야하는 상황은 거의 없다.
  • 새로운 클래스를 작성하는데 태그 필드가 등장한다면 태그를 없애고 계층 구조로 대체하는 방법을 생각해보자.
  • 기존 클래스가 태그 필드를 사용하고 있다면 계층 구조로 리팩터링 하는걸 고민하자
  • Link : https://jyami.tistory.com/85
 

[Effective Java] item23. 태그 달린 클래스보다는 클래스 계층 구조를 활용하라하라

1. 태그 달린 클래스 쓸데없는 코드가 너무 많다 : 열거 타입선언, 태그 필드, switch문 장황하고 오류를 내기 쉽고 비효율적이다. 클래스 계층 구조를 어설프레 흉내낸 것이다. class Figure { enum Shape { RECT..

jyami.tistory.com

 

item24. 멤버 클래스는 되도록 static으로 만들어라

  • 중첩 클래스에는 4가지가 있으며 각각의 쓰임이 다르다
  • 멤버 클래스 : 메서드 밖에서도 사용해야하거나, 메서드 안에 정의하기 너무 길때
  • 비정적 멤버 클래스 : 멤버 클래스의 인스턴스 각각이 바깥 인스턴스를 참조할 때 / 그렇지 않으면 정적 멤버 클래스
  • 익명 클래스 : 중첩 클래스가 한 메서드 안에서만 쓰이면서 그 인스턴스를 생성하는 지점이 단 한곳이고, 해당 타입으로 쓰기에 적합한 클래스나 인터페이스가 있을 때 / 그렇지 않으면 지역 클래스
  • Link : https://jyami.tistory.com/86
 

[Effective Java] item24. 멤버 클래스는 되도록 static으로 만들어라용도로만 사용하라

1. 중첩클래스란 중첩 클래스(nested class) : 다른 클래스 안에 정의된 클래스 정적 멤버 클래스 (비정적) 멤버 클래스 익명 클래스 지역클래스 정적 멤버 클래스를 제외한 나머지는 내부 클래스라고 한다 (inner..

jyami.tistory.com

 

item25. 톱레벨 클래스는 한 파일에 하나만 담으라

  • 소스파일 하나에는 반드시 톱레벨 클래스(인터페이스)를 하나만 담자
  • 이규칙만 따른다면 컴파일러가 한 클래스에 대한 정의를 여러개 만들어내는 일은 사라진다.
  • 소스 파일을 어떤 순서로 컴파일하든 바이너리 파일이나 프로그램의 동작이 달라지는 일은 결코 일어나지 않는다.
  • Link : https://jyami.tistory.com/87
 

[Effective Java] item25. 톱레벨 클래스는 한 파일에 하나만 담으라

소스파일 하나에 톱레벨 클래스를 여러개 선언하더라도 자바 컴파일러는 불평하지 않는다. 다만 위와 같이 이름이 중복되는 경우 컴파일 에러가 발생하게된다. 컴파일러에 어느 소스파일을 먼저 건네느냐에 따라..

jyami.tistory.com

 

댓글

Comments