본문 바로가기

Language61

직렬화 인스턴스 수를 통제해야 한다면 readResolve보다는 열거 타입을 사용하라 싱글턴 클래스를 implements Serializable을 추가하는 순간 싱글턴이 아니게 된다. readObject를 통해 클래스가 초기화될 때 별개의 인스턴스를 다시 만들기 때문이다. readResolve를 사용하게 되면 싱글톤을 유지할 수 있고 새로 생긴 인스턴스는 가비지 컬렉터의 대상이 된다. 싱글턴 필드 외에 다른 필드들은 transient로 선언해야한다. public class Elvis implements Serializable { public static final Elvis INSTANCE = new Elvis(); private Elvis() { } private String[] favoriteSongs = .. 2022. 9. 4.
직렬화 커스텀 직렬화 형태를 고려해보라. 먼저 고민해보고 괜찮다고 판단될 때만 기본 직렬화 형태를 사용하라. 기본 직렬화 형태는 유연성, 성능, 정확성 측면에서 신중히 고민한 후 합당할 때만 사용해야한다. 기본 직렬화 형태와 거의 같은 결과가 나올 경우에만 기본 형태를 사용해야 한다. 객체의 물리적 표현과 논리적 내용이 같다면 기본 직렬화 형태라도 무방하다. 성명은 논리적으로 이름, 성, 중간이름이라는 3개의 문자열로 구성되며, 앞 코드의 인스턴스 필드들은 이 논리적 구성요소를 정확히 반영했다. 기본 직렬화 형태가 적합하다고 결정했더라도 불변식 보장과 보안을 위해 readObject메서드를 제공해야 할 때가 많다. public class Name implements Serializable { /** * 성, n.. 2022. 9. 3.
직렬화 자바 직렬화의 대안을 찾으라. 자바의 역직렬화는 명백하고 현존하는 위험이다. 이 기술은 지금도 애플리케이션에서 직접 혹은, 자바 하부시스템(RMI, JMX, JMS)을 통해 간접적으로 쓰이고 있기 때문이다. 신뢰할 수 없는 스트림을 역직렬화하면 원격 코드 실행(RCE), 서비스 거부(DoS)등의 공격으로 이어질 수 있다. 잘못한게 아무것도 없는 애플리케이션이라도 이런 공격에 취약해질 수 있다. 직렬화의 근본적인 문제는 공격 범위가 너무 넓고 지속적으로 더 넓어져 방어하기 어렵다는 점이다. ObjectInputStream의 readObject 메서드를 호출하면서 객체 그래프가 역직렬화되기 때문이다. readObject는 거의 모든 타입의 코드를 수행 할 수 있다. 가젯(써드파티 라이브러리)을 통해 역직렬화.. 2022. 9. 3.
동시성 스레드 안전성 수준을 문서화하라. 한 메서드가 여러 메서드를 호출할 때 그 메서드가 어떻게 동작하느냐는 클라이언트와의 중요한 계약이다. API문서에 아무런 언급도 없으면 그 클래스 사용자는 나름의 가정을 해야만 한다. 멀티스레드 환경에서도 API를 안전하게 사용하게 하려면 클래스가 지원하는 스레드 안전성 수준을 정확히 명시해야 한다. 불변(immutable) : 이 클래스의 인스턴스는 마치 상수같아서 외부 동기화가 필요없다. ( String, Long, BigInteger ) 무조건적 스레드 안전 : 인스턴스가 수정 될 수 있으나 내부에서 동기화하여 외부 동기화 없이 사용해도 안전하다. ( AtomicLong, ConcurrentHashMap ) 조건부 스레드 안전 : 무조건적 스레드 안전과 같으나 일부.. 2022. 9. 2.
동시성 스레드보다는 실행자, 태스크, 스트림을 애용하라. java.util.concurrent패키지는 실행자 프레임워크라고 하는 인터페이스 기반의 유연한 실행 기능을 담고 있다. ExecutorService exec = Executors.newSingleThreadExecutor(); exec.execute(runnable); exec.shutdown(); 실행자 서비스의 주요한 기능 특정 태스크가 완료되기를 기다린다. 태스크 모음 중 아무것 하나(invokeAny 메서드) 혹은 모든 태스크(invoke All 메서드)가 완료되기를 기다린다. 실행자 서비스가 종료하기를 기다린다(awaitTermination 메서드). 완료된 태스크들의 결과를 차례로 받는다(ExecutorCompletionService 이용)... 2022. 8. 27.
동시성 과도한 동기화는 피하라 응답불가와 안전 실패를 피하려면 동기화 메서드나 블록 안에서는 제어를 절대로 클라이언트에 양도하면 안된다. ObservableSet클래스 import java.util.ArrayList; import java.util.Collection; import java.util.Set; public class ObservableSet extends ForwardingSet { public ObservableSet(Set s) { super(s); } private final List observers = new ArrayList(); public void addObserver(SetObserver observer) { synchronized (observers) { observers.add.. 2022. 8. 24.
동시성 공유 중인 가변 데이터는 동기화해 사용하라 synchronized 키워드는 해당 메서드나 블록을 한번에 한 스레드씩 수행하도록 보장한다. 한 객체가 일관된 상태를 가지고 생성되고 이 객체에 접근하는 메서드는 락을 건다. 그리고 다른 일관된 상태로 변화시킨다. 동기화를 제대로 사용한다면 이 객체의 상태가 일관되지 않은 순간을 볼 수 없다. 동기화는 일관성이 깨진 상태를 볼 수 없게 하다. 동기화된 메서드나 블록에 들어간 스레드가 같은 락의 보호하에 수행된 모든 이전 수정의 최종 결과를 보게 해준다. 자바에서는 스레드가 필드를 읽을 떄 항상 '수정이 완전히 반영된' 값을 얻는다고 보장하지만, 한 스레드가 저장한 값이 다른 스레드에게 '보이는가'는 보장하지 않는다. 따라서 동기화는 배타적 실행뿐 아니라 스레드.. 2022. 8. 22.
예외 메서드가 던지는 모든 예외를 문서화하라. 메서드가 던지는 예외는 그 메서드를 오바로 사용하는 데 아주 중요한 정보다. 따라서 문서화하는데 충분한 시간을 쏟아야 한다. 검사 예외는 항상 따로따로 선언하고, 각 예외가 발생하는 상황을 자바독의 @throws 태그를 사용하여 정확히 문서화하자. 공통 상위 클래스 하나로 뭉뚱그려 선언하는 일은 삼가자( Exception, Throwable ). 사용자에게 어떤 예외인지 정확히 전달이 안될 뿐더러 다른 예외들을 삼켜버릴 수 있다. main메서드는 JVM만이 호출하므로 Exception을 던지도록 선언해도 괜찮다. 비검사 예외들을 문서화하여 메서드를 작성할시 전제조건을 명시하자. 메서드가 던질 수 있는 예외를 각각 @throws 태그로 문서화하되, 비검사 예외는 메.. 2022. 8. 19.
예외 필요 없는 검사 예외 사용은 피하라. 검사예외는 발생한 문제를 프로그래머가 처리하여 안전성을 높이게끔 해주지만, 과하게 사용하면 오히려 쓰기 불편한 API가 된다. API를 제대로 사용해도 발생 할 수 있는 예외이거나 프로그래머가 의미있는 조치를 위할 수 있는 경우라면 이 정도 부담쯤은 받아들일 수 있을 것이다. 여기에 해당되지 않다면 비검사 예외가 좋다. 옵셔널을 반환하는 방법도 생가해볼 가치가 있다. try-catch문을 사용해야 되면 스트림에서 사용 할 수 없다. Optional반환하는 방법도 고민해볼만 한다. 메서드를 2개로 쪼개 비검사 예외로 바꾸는 방식이 있다. 수정 전 try { obj.action(args); } catch (TheCheckedException e) { ... // 예외 상.. 2022. 8. 18.
예외 예외는 진짜 예외 상황에만 사용하라 아래 코드는 끔찍하다. 가독성도 떨어질 뿐 아니라 예외를 쓸 필요가 없다. try { int i = 0; while(true) range[i++].climbe(); } catch(ArrayIndexOutOfBoundsException e) { } 수정 후 for(Mountain m : range) m.climb(); 왜 배열의 접근을 try-catch문으로 작성한 것일까? JVM은 배열을 접근할 때마다 경계를 넘지 않는지 검사하는데 일반적인 반복문도 배열 경계에 도달하면 종료한다. 위 코드는 하지 않아도 될 예외처리를 했기 때문에 오해의 가능성이 있고 디버깅을 어렵게 한다. 잘못된 3가지 이유 예외는 예외 상황에 쓸 용도로 설계되었으므로 JVM 구현자 입장에서는 명확한.. 2022. 8. 17.
일반적인 프로그래밍 원칙 리플렉션보다는 인터페이스를 사용하라. 자바 리플렉션(java.lang.reflect)을 이용하면 프로그램에서 임의의 클래스에 접근할 수 있다. 리플렉션은 Class, Method, Field 인스턴스를 가지고 올 수 있으며 조작 할 수도 있다. 컴파일 당시에 존재하지 않던 클래스도 이용 할 수 있다. 리플렉션의 단점 컴파일타임 타입 검사가 주는 이점을 하나도 누릴 수 없다. 존재하지 않거나 접근 할 수 없는 메서드를 호출하려 시도하면 런타임 오류가 발생한다. 리플렉션을 이용하면 코드가 지저분하고 장황해진다. 성능이 떨어진다. 리플렉션을 통한 메서드 호출은 일반 메서드 호출보다 훨씬 느리다. 리플렉션이 필요한지 확실할 수 없다면 필요없을 가능성이 크다. 리플렉션은 아주 제한된 형태로만 사용해야 그 단점을 .. 2022. 8. 12.
일반적인 프로그래밍 원칙 다른 타입이 적절하다면 문자열 사용은 피하라. 문자열은 다른 값 타입을 대신하기에 적합하지 않다. 기본 타입이든 적절한 값 타입이 있으면 그거를 쓰자. 없으면 새로 하나 만드는 것이 더 좋다. 지켜지지 않는 경우가 많다! 문자열은 열거 타입을 대신하기에 적합하지 않다. 문자열 연결은 느리니 주의하라. 여러 요소가 혼합된 데이터를 하나의 문자열로 표현하는 것은 대체로 좋지 않은 생각이다. 각 요소를 개별로 접근하려면 문자열을 파싱해야 해서 느리고, 귀찮고, 오류 가능성도 커진다. equals, toString, compareTo 메서드를 제공 할 수 없다. String compoundKey = className + "#" + i.next(); 문자열은 권한을 표현하기에 적합하지 않다. 자바2에서는 스레드의.. 2022. 8. 11.
일반적인 프로그래밍 원칙 정확한 답이 필요하다면 float와 double은 피하라. 부동소수점은 근사치로 계산하도록 세삼하게 설계되었다. 따라서 정확한 결과가 필요할 때는 사용하면 안된다. 특히, float와 double타입은 특히 금융관련 계산과 맞지 않는다. System.out.println(1.03 - 0.42); 결과 값 : 0.6100000000000001 System.out.println(1.00 - 9 * 0.10); 결과 값 : 0.09999999999999998 금융 계산에는 BigDecimal, int 혹은 long을 사용해야 한다. public static void main(String[] args) { double funds = 1.00; int itemsBought = 0; for (double price.. 2022. 8. 10.
일반적인 프로그래밍 원칙 지역변수의 범위를 최소화하라. 지역변수의 유효범위를 최소로 줄이면 코드 가독성과 유지보수성이 높아지고 오류 가능성은 낮아진다. 지역변수의 범위를 줄이는 가장 강력한 기법은 '가장 처음 쓰일 때 선언하기'이다. - 사용하기 너무 앞서 선언해주면 코드가 어수선해져 가독성이 떨어진다. 변수를 실제로 사용하는 시점엔 타입과 초깃값이 기억나지 않을 수도 있다. 또, 실제 사용하는 블록 바깥에 선언된 변수는 그 블록이 끝나 ㄴ뒤까지 살아있게 되어 끔찍한 결과로 이어질 수 있다. 거의 모든 지역변수는 선언과 동시에 초기화해야 한다. - 초기화에 필요한 정보가 충분하지 않다면 충분해질 때까지 선언을 미뤄야 한다. try-catch문은 이 규칙에서 예외다. 변수를 초기화하는 표현식에서 검사 예외를 던질 가능성이 있다면 .. 2022. 8. 8.
메서드 가변인수는 신중히 사용하라 가변인수는 인수를 0개 이상 받을 수 있도록 도와준다. static int sum(int... args) { int result = 0; for(int arg : args) result += arg; return result; } 가변인수 메서드를 호출하면, 가장 먼저 인수의 개수와 길이가 같은 배열을 만들고 인수들을 이 배열에 저장하여 가변인수 메서드에 건네준다. 신중하게 써야 하는 점은 아래코드를 예시로 들 수 있다. 아래코드는 소스가 지저분해질 뿐 아니라 컴파일타임이 아닌 런타임에 실패한다는 점이다. static int sum(int... args) { if(args.length == 0) throw new IllegalArgumentException("인수가 1개 이상 .. 2022. 8. 4.
메서드 메서드 시그니처를 신중히 설계하라 API 설계 요령 메서드 이름을 신중히 짓자. 항상 표준 명명 규칙을 따라야 한다. 편의 메서드를 너무 많이 만들지 말자. 너무 많은 메서드는 익히고, 사용하고, 문서화, 테스트, 유지보수하기 너무 어렵다. 매개변수 목록은 짧게 유지하자. 4개 이하가 좋다. 4개가 넘어가면 매개변수를 전부 기억하기 쉽지 않다. 특히 같은 타입의 매개변수를 여러 개가 연달아 나오는 경우는 많이 해롭다. 1. 여러 메서드로 쪼갠다. - 쪼개진 메서드 각각은 원래 매개변수 목록 부분집합을 받는다. 잘못하면 메서드가 너무 많아질 수 있지만, 직교성을 높여 오히려 메서드 수를 줄여주는 효과도 있다. * 직교성이란? 공통점이 없는 기능들이 잘 분리되어 있다. 2. 매개변수를 여러 개를 묶어주는 도.. 2022. 8. 1.
메서드 매개변수가 유효한지 검사하라. 메서드와 생성자 대부부은 입력 매개변수의 값이 특정 조건을 만족하여야 한다. 오류를 발생한 즉시 잡지 못하면 해당 오류를 감지하기 어려워지고, 감지하더라도 오류의 발생 지점을 찾기 어려워진다. 매개변수가 잘못되었을 때 문제점 메서드가 수행되는 중간에 모호한 예외를 던지며 실패 할 수 있다. 메서드가 잘 수행되지만 잘못된 결과를 반환할 때다. 어떤 객체를 이상한 상태로 만들어놓아서 미래에 알 수 이 없는 시점에 이 메서드와 관련 없는 오류를 낸다. public과 protected 메서드는 매개변수 값이 잘못됐을 때 던지는 예외를 문서화해야 한다. ( @throws 자바독 태그를 사용하면 된다. ) /** * ( 현재 값 mod m ) 값을 반환한다. 이 메서드 * 항상 음이 .. 2022. 8. 1.
람다와 스트림 반환 타입으로는 스트림보다 컬렉션이 낫다. 스트림은 반복을 지원하지 않는다. 스트림과 반복을 알맞게 조합해야 좋은 코드가 나온다. Stream인터페이스는 Iterable 인터페이스가 정의한 추상 메서드를 전부 포함할 뿐 아니라 Iterable 인터페이스가 정의한 방식대로 동작한다. 아래코드는 자바에서 타입추론이 안되게 때문에 컴파일 에러가 난다. for(ProcessHandle ph : ProcessHandle.allProcesses()::iterator) { // 프로세스르르 처리한다. } 아래코드는 문법적으로는 맞지만 직관성이 떨어진다. for(ProcessHandle ph : (Iterable) ProcessHandle.allProcesses()::iterator) { // 프로세스르르 처리한다. .. 2022. 7. 29.
람다와 스트림 스트림은 주의해서 사용하라. 스트림 API는 다량의 데이터 처리 작업을 돕고자 추가되었다. 스트림은 데이터 원소의 유한 혹은 무한 시퀀스를 뜻한다. 스트림 파이프라인은 이 원소들로 수행하는 연산 단계를 표현하는 개념이다. 스트림 파이프라인 스트림 파이프라인은 소스 스트림에서 시작해 종단 연산으로 끝나며, 그 사이에 하나 이상의 중간 연산이 있을 수 있다. 중간 연산은 스트림을 어떠한 방식으로 변환한다. 지연 평가 된다. 평가는 종단 연산이 호출될 때 이뤄지며 종단 연산에 쓰이지 않는 데이터 원소는 계산에 쓰이지 않는다. 종단 연산이 없는 스트림 파이프라인은 아무일도 하지 않는 명령어인 no-op과 같으므로 종단 연산을 빼먹는 일이 절대 없도록 하자. 스트림은 가독성이 어려워 유지보수가 어려워질 수 있다... 2022. 7. 26.
람다와 스트림 익명 클래스보다는 람다를 사용하라 람다의 장점 1. 코드의 가독성이 좋아진다. 2. 매개변수 타입을 생략해도 된다. 익명 클래스 Collections.sort(words, new Comparator() { public int compare(String s1, String s2) { return Integer.compare(s1.length(), s2.length()); } }) 람다 Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()) ) 생성 메서드 활용 Collections.sort(words, comparingInt(String::length)); words.sort(comparingInt(String::lengt.. 2022. 7. 24.
열거 타입과 애너테이션 @Override 애너테이션을 일관되게 사용하라. @Override는 메서드 선언에만 달 수 있으며, 이 애너테이션이 달렸다는 것은 상위 타입의 메서드를 재정의했음을 뜻한다. 잘 사용하게 되면 여러가지 악명 높은 버그들을 예방해준다. public class Bigram { private final char first; private final char second; public Bigram(char first, char second) { this.first = first; this.second = second; } public boolean equals(Bigram b) { return b.first == first && b.second == second; } public int hashCode() { .. 2022. 7. 23.
열거 타입과 애너테이션 명명 패턴보다 애너테이션을 사용하라 명명 패턴보다 애너테이션을 사용해야 하는 이유가 여러가지 있다. 오타가 나면 안된다. 올바른 프로그램 요소에서만 사용되라는 보장이 없다. 프로그램 요소를 매개변수로 전달할 마땅한 방법이 없다. 애너테이션 선언 방법 애너테이션 선언에 달려있는 애너테이션을 매타 애너테이션이라고 한다. @Retention은 생애주기 관련된 것이고, @Target은 타입에 관련된 매나 애너테이션이다. @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Test { } @Target(ElementType.METHOD) : Test애노테이션을 Method에만 달 수 있다는 의미 @Retention(Re.. 2022. 7. 20.
열거 타입과 애너테이션 확장할 수 있는 열거타입이 필요하며 인터페이스를 사용하라 타입 안전 열거 패턴과 열거 타입은 차이점이 있다. 열거 타입을 확장하게 된다면 고려할 요소가 늘어나 설계와 구현이 더 복잡해진다. 타입 안전 열거 패턴 열거 타입 확장 가능 O X 값 추가 O X 하지만, 확장 할 수 있는 열거 타입은 연산코드에서 어울린다. Operation 인터페이스에 apply 메서드를 정의해놓고 BasicOperation에서 구현하도록 강제한다. public enum BasicOperation implements Operation { PLUS("+") { public double apply(double x, double y) { return x + y; } }, MINUS("-") { public double apply(d.. 2022. 7. 18.
열거 타입과 애너테이션 int 상수 대신 열거 타입을 사용하라 열거 타입은 일정 개수의 상수 값을 정의한 다음, 그 외의 값은 허용하지 않는 타입이다. 자바 열거타입의 특징 자바 열거타입은 다른 언어와 다르게 class이다. 상수 하나 당 자신의 인스턴스를 하나씩 만들어 public static final 필드로 제공한다. 클라이언트가 인스턴스를 직접 생성하거나 확장 할 수 없다. 열거 타입은 컴파일타임 타입 안전성을 제공한다. 공개되는 것은 필드 이름 뿐이라 순서를 바꿔도 다시 컴파일 하지 않아도 된다. 열거 타입의 toString 메서드는 출력하기에 적합한 문자열을 내어준다. 임의의 메서드 / 필드 / 인터페이스를 추가 할 수도 있다. 태양계의 열거타입 public enum Planet { MERCURY( 3.302e+23.. 2022. 7. 13.
제네릭 제네릭과 가변인수를 함께 쓸 때는 신중하라. 제네릭과 가변인수는 생각보다 잘 어우러지지 않는다. 아래코드를 매개변수화 타입을 적용한다면 어떻게 될까? 제네릭은 실체화 불가 타입이기 때문에 타입 관련 정보를 적게 담고 있어 컴파일 타임에 에러를 못 낼 수 있다. 이처럼 타입 안전성이 깨지니 제네릭 varargs배열 매개변수에 저장하는 것은 안전하지 않다. static void dangerous(List... stringLists) { List intList = List.of(42); Object[] objects = stringLists; objects[0] = intList; // 힘오염 발생 String s = stringLists[0].get(0); // ClassCastException } 제네릭 .. 2022. 6. 26.
제네릭 이왕이면 제네릭 타입으로 만들라 Stack을 제네릭 타입으로 만들어 보자. public class Stack { private Object[] elements; private int size = 0; private static final int DEFALUT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFALUT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if(size == 0) throw new EmptyStackException(); Object result = elemen.. 2022. 6. 19.
제네릭 로 타입은 사용하지 말라 로 타입을 쓰게 되면 컴파일 타임에 오류를 알기 힘들다. 문법적으로 막아놓지 않았지만 절대로 쓰면 안된다. 로 타입을 쓰면 제네릭이 안겨주는 안전성과 표현력을 모두 잃게 된다. 로 타입은 안되지만 List처럼 임의 객체를 허용하는 매개변수화 타입은 괜찮다. List에 List을 넘길 수 있지만 List는 안된다. List은 List의 하위타입이지만, List는 아니기 때문이다. 그렇다면 제네릭 타입을 쓰고 싶지만 매개변수 타입을 신경쓰고 싶지 않다면 어떻게 해야 할까? class를 사용하는 것이다. Collection에는 null이외에는 어떤 원소도 넣을 수 없다. 하지만, class 리터럴에는 로 타입을 사용해야 한다. List.class는 허용되지 않는다. 또, instanc.. 2022. 6. 18.
클래스와 인터페이스 멤버 클래스는 되도록 static으로 만들라. 중첩 클래스(nested class)란 다른 클래스 안에 정의된 클래스를 말한다. 중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 그 외의 쓰임새가 있다면 톱 레벨 클래스로 만들어야 한다. 그 외의 쓰임새가 있다면 톱 레벨 클래스로 만들어야 한다. 중첩 클랫의 종류 외부 접근 쓰임 정적 멤버 클래스 가능 외부 클래스에게 도움을 줄 때 (비정적) 멤버 클래스 불가 어댑터로 정의 할 때 익명 클래스 비정적 문맥 정적 팩토리 메서드나 작은 함수를 구현 할 때 지역 클래스 비정적 문맥 정적 멤버 클래스 정적 멤버클래스는 public static class로 정의하고 private 멤버를 외부 클래스에서 접근할 수 있다는 점만 제외하고 모두 같다. 외부 클.. 2022. 6. 17.
클래스와 인터페이스 인터페이스는 구현하는 쪽을 생각해 설계하라. 자바 8이후부터는 인터페이스도 디폴트 메서드를 구현 할 수 있게 되었지만 생각 할 수 있는 모든 상황에서 불변식을 해치지 않는 디폴트 메서드를 작성하기란 어렵다. removeIf의 예제를 보면 Predicate에서 true를 반환하는 것들을 제거한다. default boolean removeIf(Predicate 2022. 6. 17.
클래스와 인터페이스 추상 클래스보다는 인터페이스를 우선하라. 추상클래스는 정의 타입을 항상 하위 클래스로 하여야 한다. 하지만, 인터페이스같은 경우에는 어떤 클래스를 상속해도 같은 타입으로 취급한다. 기존 클래스에도 손쉽게 새로운 인터페이스를 구현해 넣을 수 있다. 인터페이스가 요구하는 메서드를 추가하고, 클래스 선언에 implements 구문만 추가하면 끝이다. 반면 추상클래스는 확장하기 어렵다. 추상 클래스를 확장하기 원한다면 상속 받는 모든 클래스의 공통조사이여야 한다. 적절하지 않은 상황에도 강제로 해야만 하는 상황이 생긴다. 인터페이스는 믹스인(mixin)정의에 안성맞춤이다. 믹스인 정의는 클래스가 구현 할 수 있는 타입으로 부모클래스가 되지 않으면서 선택적 행위를 제공해준다. Comparable은 자신을 구현한 .. 2022. 6. 14.