사다리 미션에서 다음처럼 Line
을 생성하는 코드를 작성했다. 반복문에서 이루어지는 행위를 직관적인 makeConnection
이라는 이름을 붙이고 싶어 메서드를 작성하였다.
코드1
public class Line {
private List<Point> line;
public Line(int player) {
line = new ArrayList<>();
for (int i = 0; i < player - 1; i++) {
line.add(new Point(isConnection(i)));
}
}
}
다음은 메서드를 사용해 수정한 모습이다.
코드2
public class Line {
private List<Point> line;
public Line(int player) {
line = new ArrayList<>();
makeConnection(player, line);
}
private void makeConnection(int player, ArrayList line) {
for (int i = 0; i < player - 1; i++) {
line.add(new Point(isConnection(i)));
}
}
}
하지만 이렇게 작성했을 때
makeConnection(player, line);
의 line
에 다음처럼 빨간줄이 나온다.
required type: ArrayList
Provided: List <model.Point>
본인은 line = new ArrayList<>();
라는 코드에서 line을 ArrayList 형으로 지정했기 때문에 ArrayList 형으로 line을 받는 makeConnection이 잘 작동할 것이라고 생각했다.
하지만 코드2가 작동하지 않는 이유는 무엇일까?
답은 List는 인터페이스이고, 메서드에서 지정한 ArrayList는 구체클래스라는 점이다.
인터페이스(interface)는 다른 클래스를 작성할 때 기본이 되는 틀을 제공하면서, 다른 클래스 사이의 중간 매개 역할을 담당한다. 인터페이스인 List는 구현체로 ArrayList 클래스, LinkedList 클래스, Vector 클래스를 갖기 때문에 다양하게 올 수 있다.
ArrayList라는 구체클래스 파라미터 타입이 지정된 메서드는 List 인터페이스의 다형성으로 ArrayList가 아닌 다른 구체 클래스를 넘겨받을 수 있기 때문에 실행할 수 없다.
이와 관련하여 이펙티브 자바 64. 객체는 인터페이스를 사용해 참조하라를 함께 읽어보자.
첵에서는 적합한 인터페이스만 있다면 매개변수뿐 아니라 반환값, 변수, 필드를 전부 인터페이스 타입으로 선언하라고 한다. 실제 클래스를 사용할 때는 ‘오직' 생성자로 생성할 때만 한정한다.
// 좋은 예시: 인터페이스를 타입으로 사용한다.
Set<Son> sonSet = new LinkedHashSet<>();
// 나쁜 예시: 클래스를 타입으로 사용한다.
LinkedHashSet<Son> sonSet = new LinkedHashSet<>();
구체 클래스 대신 인터페이스를 사용하는 습관은 프로그램을 유연하게 작성하도록 합니다. 만약 나중에 LinkedHashSet
이 아닌 HashSet
을 사용하라는 요구사항이 온다면 HashSet
클래스로 바꾸면 된다. 다른 코드는 바꿀 필요 없다.
LinkedHashSet<Son> sonSet = new HashSet<>();
다만 주의해야할 점은 원래 클래스가 인터페이스가 가지고 있는 규약 외에 다른 특별한 기능이 있고, 그 기능을 다른 코드들이 사용한다면 새로운 클래스를 사용할 때도 이 기능을 제공해야 한다.
예를 들어, LinkedHashSet
이 따르는 순서 정책을 가정하고 동작하는 상황에서 HashSet
으로 바꾸면 HashSet
은 반복의 순회 순서를 보장하기 않기 때문에 문제가 될 수 있다.
만약 다음과 같이 적합한 인터페이스가 없다면 클래스로 참조해야 한다.
String
과 BigInteger
같은 클래스OutputStream
등 [java.io](http://java.io)
패키지의 여러 클래스 등PriorityQueue
클래스는 Queue
인터페이스에는 없는 Comparator
메서드를 제공위의 경우가 모든 상황을 설명하지 못한다. 하지만 적합한 인터페이스가 없다면 클래스의 계층구조 중 필요한 기능을 만족하는 상위의 클래스를 타입으로 사용하는 것이 필요하다.
그럼 다음과 같이 코드를 수정해보자. makeConnection(int player, ArrayList line)
에서 ArrayList
를 List
로 수정하자. 근데 과연 이 코드는 동작할 것인가?
결론부터 말하자면 List<Point>
와 같이 타입을 지정해줘야 한다. 다음 포스팅에서 자세히 알아보자.
public class Line {
private List<Point> line;
public Line(int player) {
line = new ArrayList<>();
makeConnection(player, line);
}
private void makeConnection(int player, List<Point> line) {
for (int i = 0; i < player - 1; i++) {
line.add(new Point(isConnection(i)));
}
}
}
구체 클래스 대신 인터페이스를 사용한다.
적합한 인터페이스가 없다면 클래스의 계층구조 중 필요한 기능을 만족하는 상위의 클래스를 타입으로 사용한다.