지난 시간에는 객체지향적으로 좋은 프로그램을 만들기 위한 중급 문법과 그 원리에 대해서 배웠다. 현재 우리가 설계를 할 순 없지만, 해당 문법들을 만져보면서 익숙해지고 해석하는 방법을 알아야 한다.
그럼 간단히 지난 시간을 리뷰하자.
1. 상속
프로그램을 데이터와 UI를 구분하여 만들게 되면 기존에 서로 엉켜있던 기능과 코드들이 독립성을 가져서 유지, 보수 효율이 높아진다.
또한, 한 클래스 안에 작성된 코드들이 분리됨으로써 가독성이 올라간다. 이를 위해서 사용하는 문법이 ‘상속’이다.
2. 추상화
상속 관계에선 굳이 정의할 필요가 없는 메서드와 변수들이 존재한다.
이것은 자식 클래스에서 ‘오버라이딩’ 되거나, 정의될 목적으로 작성된 컴파일러에 대한 ‘표기’이다. abstract
라는 키워드를 붙여서 추상화 처리를 하여, 실행부를 작성할 필요가 없다.
다만 추상화된 것이 존재하면, 해당 클래스 자체가 추상화되어야 한다.
따라서 개발자가 상속받은 클래스를 이용하면 추상화된 것을 반드시 입력해야 한다.
이 원리는 주로 프레임워크가 개발자에게 코드를 강요하는 방식으로 쓰인다.
3. 다형성
상속 관계의 특징 중에 하나다. 기존에 변수에는 같은 자료형만 담을 수 있다는 것이 법칙이었다.
하지만 상속 안에선 상위 클래스의 참조변수에는 하위 클래스의 주소값을 담을 수 있다.
다시 말하면 객체를 생성할 수 있다.
다만 상속과 약간 다르다. 상속이 자식 클래스의 생성 시 인스턴스 내부에 부모 인스턴스가 기본적으로 깔리는 것이라면 다형성은 그 원리를 이용해, 부모 인스턴스의 참조변수에 자식 인스턴스를 생성할 수 있다는 것이다.
단, 자식 인스턴스의 기능은 사용할 수 없다. 사용하려면 ‘오버라이딩이나, DownCasting’을 하면된다.
추가로 다형성이 가능한 것은 ‘가상함수테이블’ 때문이다.
A를 상속받은 B를 만들더라도 실제 만들어진 것은 B 인스턴스이다.
따라서 B의 테이블에 입력된 코드의 주소들은 A의 기능을 가진 또 다른 주소이다.
그저 A를 품고 있을 뿐, 그것은 B란 사실을 이해해야 한다.
- 데이터 저장소 문제
- Collection
- ArrayList
지난 실습에서 데이터를 저장할 공간으로 ‘배열’을 택했다.
그러나 개발자는 사용자가 몇 개의 데이터를 입력할지 모른다.
따라서 공간이 정해진 배열은 큰 문제이다.
원래는 데이터의 초과입력 문제를 try-catch
로 해결시켰다.
구체적으로 배열의 인덱스를 초과하면 일어나는 예외를 활용하여 catch에서 새로운 배열을 선언하고, 기존의 값을 옮겨 담는 방법을 사용하였다.
하지만 이는 데이터가 점점 거대해질수록, 옮겨 담는 과정에서 비효율이 생겨난다.
겨우 한 개의 데이터만 초과해도, 옮겨 담는 작업을 또 해야 한다.
또, 배열의 중간값이 삭제되거나, 배열의 길이를 줄이고자 하면?
결국 넘어가는 것만 생각했지, 그 외로는 능동적으로 사용할 수 없다.
배열의 능동성을 추구하는 배열을 ‘Vector’라고 하며, ‘Dynamic Array, Smart Array’가 있다. 둘에겐 ‘thread safety’의 문제가 있다.
전자는 안전하지만 성능이 떨어지고, 후자는 안전하진 않지만 성능이 좋다.
자바에선 ArrayList
라 부르는데, 아예 똑같은 것은 아니다.
이를 극복하기 위해서 만든 게 ‘LinkedList’인데, 일단 배열이 아니다.
변수마다 다음 데이터의 주소를 입력해줘서 이를 따라서 데이터에 접근하는 원리를 사용하는데, 변수와 다르게 이름은 첫 번째 변수에만 명명하다.
하지만 이 역시 데이터가 커지면 탐색하는 굉장한 비효율을 유발한다.
그래서 입력 속도를 포기하고, 탐색을 목적으로 만든 것이 Tree와 Hash이다.
이렇게 데이터를 다루는데, 다양한 자료구조들이 있고 이를 지원하는 라이브러리들을 ‘Collection Framework’ 라고 한다.
자바에선 다음의 네 가지의 유형으로 분류된다.
1) 순서가 있는 목록인
List
2) 순서가 안 중요한 목록인Set
3) FIFO의Queue
4) 키와 값으로 저장하는Map
우리가 이제부터 사용할 것은 ‘ArrayList’ 라는 배열이다.
일단 ArrayList 는 모든 자료형이 들어갈 수 있다.
또한, 생성 시 배열의 길이를 정하지 않는다. 이를 통해서 다음 두 가지를 알 수 있다.
1) ArrayList로 만든 배열의 기본 자료형은 Object
이다.
배열은 그 배열의 자료형에 맞는 값을 저장한다. 그러나 ArrayList 모든 자료형을 저장할 수 있다.
이는 ArrayList의 기본 자료형이 Object(객체)라는 클래스이기 때문이다.
그럼 Object는 뭐냐?
Java의 모든 클래스에는 부모 클래스가 있다.
즉, 모든 클래스가 상속받는 부모 클래스가 있는데 그것이 Object
이다.
따라서 암묵적으로 모든 클래스 안에는 extends Object
가 사용된다.
근데, 상속의 규칙 중 하나가 여러 클래스를 상속 받을 수 없다는 것이다.
따라서 extends
를 사용하는 순간 Object
는 사라진다.
하지만 모든 자료형이 Object
를 상속받기 때문에 결과적으로 연결된다.
여기서 이제 ‘다형성’이 적용된다. 다형성은 상위 클래스의 참조변수가 하위 클래스의 주소값을 저장할 수 있다는 원리이다.
가장 최상 Object
로 다형성로 인해 하위 클래스의 참조변수를 넣을 수 있다.
다만, 상위 클래스의 기능만 쓸 수 있음으로 능동적으로 { implement / overriding / down casting }을 사용한다.
2) ArrayList는 능동적으로 길이가 늘어난다.
ArrayList는 길이를 정하지 않는다. 다만 우리가 .add()
메서드를 통해 인스턴스를 추가를 하면 자동으로 인덱스가 늘어난다.
ArrayList arr = new ArrayList();
arr.add(instance);
정말 여러모로 편한 기능이다.
다만 ArrayList에 여러 타입의 인스턴스를 넣어 쓰는 경우는 극히 드물다.
그래서 사용하는 것이 Generic
이다.
ArrayList<Integer> arr = new ArrayList<>();
arr.add(Integer instance);
이렇게 하면 정수형 배열이 만들어져서, 전반적으로 형변환없이 Integer
의 인스턴스들을 연결해서 사용할 수 있다.
여기서 정수형이면 int
아닌가요? 할텐데, 인스턴스를 사용할 것이기에 클래스는 Integer
가 맞다.