Notepad


  • 홈

  • 아카이브

  • 태그

[Effective Java] Comparable 구현을 고려하라

작성일 2017-12-14 | In Effective Java |

Comparable

이 인터페이스는 다른 인터페이스와 달리 하나의 메소드를 가지고 있다. compareTo 메소들을 가지고 있는데 이 메소드는 Object 클래스에는 포함되어 있지 않으며 오직 Comparable만이 가지고 있다. Comparable을 구현하는 클래스의 객체들은 모두 자연적 순서를 가지고 있다. 또한 최대/최소치를 계산하기도 간단하며, 그 컬렉션을 정렬된 상태로 유지하도 쉽다. 그리고 다양한 제네릭 알고리즘 및 Comparable 인터페이스를 이용하도록 작성된 컬렉션 구현체와도 전부 연동할 수 있다.

compareTo 메소드의 일반 규약

  • 자신과 주어진 객체를 비교하는데 자신이 주어진 객체보다 작으면 음수를, 같으면 0, 크다면 양수를 반환한다. 만약 비교 불가능한 객체가 전달된다면 ClassCastException 예외를 던진다.
  • 모든 x와 y에 대해 x.compareTo(y) == y.compareTo(x)는 같아야 한다. 만약 예외를 던진다면 역도 반드시 그래야한다.
  • compareTo를 구현할 때는 추이성이 만족되도록 해야한다. (x.compareTo(y) > 0 && y.compareTo(z) > 0 이면 x.compareTo(z) > 이어야한다.)
  • x.compareTo(y) == 0 이면 x.compareTo(z) == y.compareTo(z)의 관계가 모든 z에 대해 성립해야한다.
  • (x.compareTo(y) == 0) == x.equals(y) 이 규약은 강력히 추천하지만 절대적으로 요구되는 것이 아니다. 일반적으로, Compareable 인터페이스를 구현하면서 이 조건을 만족하지 않는 클래스는 반드시 그 사실을 명시해야한다.

이 규약들은 모두 한 클래스 객체 사이에 자연적 순서가 존재하기만 하면 어떤 것이든 이 규약을 만족할 것이다.

코드

public class ComparablePractice {
    public static void main(String[] args) {
        Fruit[] fruits = new Fruit[5];

        fruits[0] = new Fruit("사과", 10);
        fruits[1] = new Fruit("오렌지", 34);
        fruits[2] = new Fruit("포도", 24);
        fruits[3] = new Fruit("배", 19);
        fruits[4] = new Fruit("키위", 37);

        Arrays.sort(fruits);

        for (int i = 0; i < 5; i++) {
            System.out.println(fruits[i].getName());
        }
        System.out.println("----------------------------");

        Arrays.sort(fruits, new Comparator<Fruit>() {
            @Override
            public int compare(Fruit o1, Fruit o2) {
                return o2.getName().compareTo(o1.getName());
            }
        });

        for (int i = 0; i < 5; i++) {
            System.out.println(fruits[i].getName());
        }
    }
}

class Fruit implements Comparable<Fruit> {
    private String name;
    private int price;

    public Fruit(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

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

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public int compareTo(Fruit o) {
        return name.compareTo(o.getName());
    }
}
  • compareTo 메소드에서 사용자가 원하는 값을 기준으로 정렬해야한다.

Comparable와 Comparator 차이

서로 같은일을 하는 인터페이스지만 왜 따로 존재하냐면 Comparable는 자기 자신과 다른 객체 하나를 비교하고 Comparator은 다른 두개의 객체를 비교한다. Comparator 인터페이스를 구현한 객체를 기준으로 다른 두개의 객체를 비교한다는 말이다.

더 읽어보기 »

[Effective Java] clone을 재정의 할 때는 신중하라

작성일 2017-12-12 | In Effective Java |

Cloneable Interface

Cloneable은 어떤 객체가 복제를 허용한다는 사실을 알리는 데 쓰려고 만들어진 믹스인(mixin) 인터페이스다. 이 인터페이스는 아무런 추상 메소드들을 가지고 있지 않고 오직 Object 클래스의 clone을 사용할것인지 여부를 결정하기만 한다. clone 메소드를 호출하면 해당하는 객체의 복사본을 만들어낸다. 또한 원본객체와 같은 필드값을 가지며 필드의 값도 복사된다. 이 인터페이스를 implement를 하지않고 clone메소드를 호출하면 CloneNotSupportedException을 뱉어낸다.

clone 메소드의 일반 규약

  • x.clone() != x
  • x.clone().getClass() == x.getClass()
  • x.clone.equals(x)

이 규약들은 참이여야 하지만 꼭 그렇지만도 않다.

clone override

final이 아닌 슈퍼 클래스를 오버라이딩할 경우 반드시 서브 클래스에서 super.clone()을 호출하여 얻은 객체를 반환하야 한다. 그리고 해당하는 객체를 복사할 때 레퍼런스 타입을 가지고 있는 경우에는 레퍼런스 타입에도 clone 메소드를 호출해야한다.

class Stack implements Cloneable {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        this.elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }
        Object result = elements[--size];
        elements[size] = null;
        return result;
    }

    private void ensureCapacity() {
        if (elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }

    @Override
    public Stack clone() {
        try {
            Stack result = (Stack) super.clone();
            result.elements = elements.clone();
            return  result;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}
  • clone 메소드에서 super를 사용해서 얻어온 객체는 형변환을 해서 넣지만 elements는 clone 메소드을 호출한 결과 그대로 넣고있다. 릴리즈 1.5부터는 배열에 clone을 호출하면 반환되는 배열의 컴파일 시점 자료형은 복제 대상 배열의 자료형과 같기 때문이다.
  • clone 메소드는 동기화를 지원하지 않는다. 원한다면 따로 구현야해할 것이다.
  • clone 을 사용해 꼭 모두 복사할 필요가 없다. 다른 값을 가지고 싶은 필드가 있다면 clone 메소드에서 따로 설정하는게 좋다.
  • clone의 아키텍처는 변경 가능한 객체를 참조하는 final 필드의 일반적 용법과 호환되지 않는다. 따라서 final 선언을 지워야 할 수도 있다.
  • 깊은 복사를 진행할 때 재귀보다는 loop를 사용하는게 좋다. Stack Overflow가 발생할 수 있기 때문이다.
  • Cloneable 인터페이스를 구현하는 클래스는 제대로 동작하는 public clone 메소드를 제공해야한다.

사실 clone 메소드를 많이 사용하지 않는다. 굳이 사용해야 한다면 제대로 구현해야할 것이다.

더 읽어보기 »

[pattern] Strategy Pattern

작성일 2017-12-11 | In Pattern |

Strategy Pattern

필요한 알고리즘을 캡슐화 시켜서 상황에 맞게 사용하는 방법

더 읽어보기 »

[pattern] Adapter Pattern

작성일 2017-12-09 | In Pattern |

Adapter Pattern

약간의 차이가 있는 클래스들을 인터페이스를 통해 똑같은 기능으로 사용하는 방법

더 읽어보기 »

[pattern] Iterator Pattern

작성일 2017-12-08 | In Pattern |

Iterator Pattern

for나 while를 대신하여 사용할 수 있는 도구

더 읽어보기 »

[pattern] Factory Pattern

작성일 2017-12-07 | In Pattern |

Factory Pattern

클래스의 생성을 다른 클래스의에 위임하는 것

더 읽어보기 »

[pattern] Singleton Pattern 잘쓰기

작성일 2017-12-06 | In Pattern |

Eager Initialization

```java class EagerInitialization { // private static 로 선언. private static EagerInitialization instance = new EagerInitialization();

더 읽어보기 »

[Effective Java] toString는 항상 재정의하라

작성일 2017-12-04 | In Effective Java |

toString() 메소드

java.lang.Object 클래스가 toString를 메소드를 제공한다. 이 메소드의 반환값은 클래스이름 다음에 @을 붙이고 다음에 16진수로 표시된 해시 코드가 반환된다. 이 값은 사용자에게 유용한 정보를 제공하지 않는다. toString를 재정의 해서 사용자들이 알아볼 수 있도록 해야한다.

더 읽어보기 »

[Effective Java] equals를 재정의할 때는 반드시 hashCode도 재정의하라

작성일 2017-12-01 | In Effective Java |

equals를 재정의할 때는 반드시 hashCode도 재정의하라

equals 메소드가 같은 객체라는 것을 확인하더라고 hashCode가 다르면 hash로 만들어진 다른 자료구조에서 null이라는 값을 보낸다. 이처럼 hashCode를 재정의 하지 않음으로서 많은 오류가 생길 수 있다.

더 읽어보기 »

[Effective Java] equals를 재정의할 때는 일반 규약을 따르라

작성일 2017-11-30 | In Effective Java |

equals를 재정의할 때는 일반 규약을 따르라

equals 메서드는 재정의하기 쉬워 보이지만 실수할 여지가 많고 이로인한 결과는 끔찍하다. 이런 문제는 equals를 재정의 하지않는것도 방법이긴 하지만 이 경우에는 오직 자기 자신하고만 같은객체가 된다. 다음과 같은 경우에는 그래도 된다.

더 읽어보기 »
1 2 3 4
Kim BoWoon

Kim BoWoon

https://kimbowoon.github.io/

38 포스트
3 카테고리
RSS
© 2020 Kim BoWoon
Powered by Jekyll
Theme - NexT.Muse