본문 바로가기
Back-End/Java

TIL 230607 : ArrayList와 List 사이. (feat. 업캐스팅, 다운캐스팅)

by 우인입니다 2023. 6. 7.
    private List<Room> rooms = new ArrayList<>();
    private List<Reservation> reservations = new ArrayList<>();

팀 과제 도중 다른 팀원이 작성한 코드 중 위 코드를 보고 질문했습니다.

ArrayList로 생성하고 List 타입으로 받은 이유가 뭐에요..???

A : 얘가 얘한테 상속.. 다형성이잖아요.

 

다형성인건 알지만 결국엔 그것은 그걸 표현하는 개념의 이름일 뿐이다.

교재의 예제에서도 종종 저렇게 표현하던데, 그랬을 때의 특징과 장점이 있지 않을까 궁금해졌다.

 


public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

우선 ArrayList를 자바 내 접근해보면 하나의 클래스임을 알 수 있고, List는 implements된 인터페이스임을 확인했다.

ArrayList -> 클래스     /     List -> 인터페이스

 

그렇다면, 아래 두 코드의 차이점은 무엇일까?

    private List<Room> rooms = new ArrayList<>();  // List 타입
    private ArrayList<Room> rooms = new ArrayList<>();  // ArrayList 타입

결론적으로는 같다고 한다.

하지만 기능적으로 List의 기능을 쓸 경우를 위해, LinkedList의 장점도 가져가기 위해, List를 ArrayList로 구현하고 List로 선언한 것.

 

private List<Room> rooms = new ArrayList<>();  // List 타입으로 선언하고, ArrayList로 생성해둠.
private List<Room> rooms = new LinkedList<>(); // 이후 LinkedList로 구현체가 바뀌어도 호환.

private ArrayList<Room> rooms = new ArrayList<>(); // ArrayList로 생성됨.
private List<Room> rooms = new LinkedList<>(); // 오류.

이후에 이런식으로 LinkedList로 전환하여 이용이 가능하고, 이 때 LinkedList의 장점인 빠른 추가, 수정, 삭제를 기대해볼 수 있다고 한다.

 

어떻게 활용할 지는 아직 감이 안 오긴한다.

 

 

 

추가적으로, 의도적으로 해당 클래스보다 부모클래스의 타입 혹은 그 반대의 경우로 타입을 해석하는 경우가 있는데,

이를 '업캐스팅', '다운캐스팅'이라고 한다더라.


업캐스팅, 다운캐스팅이란?

 

캐스팅이란 단어는 한글로 타입변환(형 변환, Casting)이고 앞서서 기본형 타입을 형변환을 배웠었다.

int i = 10;
double j = (double) i; // j = 10.0

 

거기서 확장되어, 참조형 타입에서도 형변환이 가능한데, 이를 참조형 캐스팅 (업캐스팅 / 다운캐스팅)이라고 부른다.

class Parent {
	String name;
	int age;
}

class Child extends Parent {
       //String name; // 상속받은 멤버1
       //int age; // 상속받은 멤버2
    
	int grade; // 자식클래스만 가지고있는 멤버
}

Parent p = new Parent(); 
Child c = new Child();

Parent p2 = new Child(); // 업캐스팅 - 기능적으로는 더 좁아진다. grade멤버 사용 불가.

Child c2 = (Child)p2; // 다운캐스팅 - p2는 Child를 참조하기 때문에 명시적으로 형변환이 가능하다.

 

업/다운캐스팅의 개념 자체는 위에 기술한 대로다.

중요한 건 각 특징과 어떤 쓰임을 갖는 지 같다.

 

 

업캐스팅의 특징

  • 업캐스팅은 캐스팅 연산자 괄호를 생략할 수 있다. (ex. Parent p2 = new Child();)
  • 단, 부모 클래스로 캐스팅 된다는 것은 멤버의 갯수 감소를 의미한다. (부모 클래스 멤버만 접근 가능)
  • 업캐스팅을 하고 메소드를 실행할때, 만일 자식 클래스에서 오버라이딩한 메서드가 있을 경우, 부모 클래스의 메서드가 아닌 오버라이딩 된 메서드가 실행되게 된다.

업캐스팅 하는 이유

 : '공통적으로 할 수 있는 부분을 만들어 간단하게 다루기 위함.'으로 정리되어 있다.

 

아래 feature와 size를 멤버로 갖고, bark라는 메소드를 갖는 Animal 클래스를 만들었다.

그리고 이를 상속받아 각 Dog, Cat, Goat 클래스를 만든 뒤 bark()메소드를 오버라이딩 했다.

 

class Animal {
   	String feature;
   	int size;
   
   	void bark(){
		System.out.println("짖다!");
 	}
}

class Dog extends Animal {
	@Override
	void bark(){
		System.out.println("짖다!");
	}
}

class Cat extends Animal {
	@Override
	void bark(){
		System.out.println("이야오옹~");
	}
}

class Goat extends Animal {
	@Override
	void bark(){
		System.out.println("곹.곹.");
	}
}

이렇게 되면 부모 자식 클래스가 형성되고 각 동물클래스를 부모클래스인 Animal클래스로 업스캐스팅이 가능하다.

Animal dog = new Dog();
Animal cat = new Cat();
Animal goat = new Goat();

Animal[] zoo = {dog, cat, goat};

 

이처럼 Animal 타입으로 선언 후 각 자식클래스로 생성이 가능했고,

zoo라는 Animal타입의 배열에 삽입해도 문제없다.

 

마지막으로 다형성의 꽃으로다가 다음 반복문을 실행해보자.

 

 

bark()하나로 각각의 결과를 얻을 수 있다니. 도파민이 분출된다.

 

다운캐스팅

다운캐스팅은 간략하게 설명해보자면,

'부모 클래스로 업 캐스팅된 자식 클래스를 복구하여, 본인의 필드와 기능을 회복하는 기능'

 

업캐스팅시에 사용할 수 없게된 멤버를 다시 되찾고 싶을 때 사용할 수 있는 것이다.

자연스럽게, 업캐스팅 된 객체만 가능하고 그렇지않으면 오류가 발생한다.

Animal fakeDog = new Animal();
Dog realDog = (Dog) fakeDog; //fakeDog는 Animal을 참조하는 객체라 다운캐스팅이 불가능하다.

System.out.println(realDog.size);

///오류메시지///
Exception in thread "main" java.lang.ClassCastException: class Animal cannot be cast to class Dog (Animal and Dog are in unnamed module of loader 'app')
	at Upcasting.main(Upcasting.java:11)