[Java] 17.1. Object & Generic 기본기

김주희's avatar
Feb 26, 2025
[Java] 17.1. Object & Generic 기본기
모든 클래스의 부모 (생략되어 있다.)
 

1. Object 타입 일치

  1. 객체 instanceof 클래스 : 객체가 특정 클래스의 인스턴스인지 확인해서 true/false 리턴
  1. 모든 클래스는 Object 클래스를 상속받기 때문에 특정 클래스를 new하게 되면 특정 클래스와 Object 클래스 둘 다 메모리에 뜨게 된다.
  1. callName(new Dog()) : callName의 매개변수가 Object u이므로 Object u = new Dog()와 같다. 메모리 heap에 Dog와 Object가 둘 다 떠있고 u는 그 중 Object를 가리키고 있는 상황이다.
  1. u instanceof Dog: Dog 클래스가 Object 클래스를 상속 받고 있고 즉, 메모리에 Dog가 떠있기 때문에 true를 반환한다.
  1. Dog d = (Dog) u; : Object 클래스의 객체 u를 Dog타입으로 다운캐스팅 할 수 있다.
  1. sout(d.name): u.name일 때는 Object 클래스가 name이라는 상태를 들고 있지 않기 때문에 사용할 수 없었지만 u를 Dog로 다운캐스팅하여 Dog 타입의 d는 Dog의 name 상태를 사용할 수 있다.
package ex17; class Dog { String name = "강아지"; } class Cat { String name = "고양이"; } // 타입 일치 public class Ob01 { static void callName(Object u) { if (u instanceof Dog) { // u가 dog 타입이라면 (메모리에 dog가 떠있는지 확인) 다운캐스팅해서 쓸 수 있음 Dog d = (Dog) u; System.out.println(d.name); } else if (u instanceof Cat) { // u가 Cat 타입이라면 (메모리에 Cat이 떠있는지 확인) 다운캐스팅해서 쓸 수 있음 Cat c = (Cat) u; System.out.println(c.name); } } public static void main(String[] args) { callName(new Dog()); // static void callName(Dog u = new Dog())와 같다. callName(new Cat()); } }
notion image
 

2. Generic 기본기

new 할 때 타입을 결정할 수 있다.
  1. Generic이란 클래스 내부에서 타입이 지정되지 않고 외부에서 사용자에 의해 결정되는 기법이다.
  1. 제네릭에는 클래스 타입만 지정 가능하다.
  1. 제네릭은 new할 때 타입을 결정할 수 있는데 지정하지 않을 경우 디폴트값으로 Object 타입이 된다.
  1. new 클래스<T>(); 이 기본 형태이지만 생략하여 new 클래스(); 로 사용 가능하다.
package ex17; class Box<T> { // 컨밴션 - 대문자로 작성. 보통 Type의 T T data; } public class Ge01 { public static void main(String[] args) { Box<String> b = new Box(); // new할 때 안 정하면 Object 타입이 됨. b.data = "Hello"; // new 할 때마다 type 결정 가능 = generic Box<Integer> i = new Box(); // new 이후에는 생략 가능 i.data = 10; } }
 
 

3. Object, Generic 저장 방식의 차이가 무엇일까?

  1. Object와 제네릭 둘 다 기본 자료형을 저장하는 것은 동일하다. (무엇이든 사용 가능하다.)
  1. Object와 Generic를 각각 사용해야하는 경우가 다르다
    1. Object를 쓸 수 밖에 없는 경우
    2. 제네릭을 써야 되는 경우 ( 제네릭으로 저장하는 것이 유리하다.)
package ex17; // A회사가 데이터 저장하는 프로그램 만듦 -> 누군가에게 팔았음 class ACom<T> { void save(T data) { System.out.println(data + "가 저장되었습니다."); } } class BCom { void save(Object data) { System.out.println(data + "가 저장되었습니다."); } } // 타입 일치? 동적 바인딩이 더 좋은 거 아닌가? // 1. Object를 쓸 수 밖에 없는 경우 // 2. 제네릭을 써야 되는 경우 (무조건 제네릭으로 저장하는 것이 유리하다.) public class Ob02 { public static void main(String[] args) { ACom<String> ac = new ACom(); ac.save("문자열"); BCom bc = new BCom(); bc.save("문자열"); } }
 
 

4. 데이터 저장은 Generic이 무조건 유리하다.

new 할 수 있다면!
  • 제네릭은 다운 캐스팅을 할 필요가 없다. (단, new를 할 수 있어야 제네릭을 사용 가능하다)
package ex17; class Teacher extends Object { private String name = "선생님"; public String getName() { return name; } } class Student extends Object { private String name = "학생"; public String getName() { return name; } } class FCom { Object data; void save(Object u) { data = u; } } class KCom<T> { T data; void save(T u) { data = u; } } // 왜 제네릭이 유리한가? -> 다운 캐스팅이 필요없다. public class Ge02 { public static void main(String[] args) { FCom f1 = new FCom(); f1.save(new Teacher()); Teacher t1 = (Teacher) f1.data; System.out.println(t1.getName()); KCom<Student> k1 = new KCom(); k1.save(new Student()); System.out.println(k1.data.getName()); } }
 
 

5. new 할 수 없다면 Object로 저장

가령 Singleton 패턴일때!
미리 클래스를 메모리에 올려놓아야 할 때, 무슨 타입이 들어올지 모르겠다면 Object 타입으로 저장한다.
  1. new를 할 수 없어서 제네릭을 사용할 수 없는 경우 Object를 사용해야한다.
  1. private static Session session = new Session(); : Session을 다른 클래스 등에서 new 못하지만 단 한 번 메모리 상에 올려놓아야 할 때, private과 static으로 설정하여 해당 클래스 내부에서 new를 하면 단 한 개의 객체가 main 실행 전 생성되고 main에서 new 제어권을 가지게 된다. ⇒ 싱글톤
      • 싱글톤 패턴 → 기본 생성자: private / 메소드: static
  1. Session session = Session.getSession(); : getSession() 메서드를 통해 session 싱글톤 객체를 가져온다.
  1. session.setAttribute(new Banana()); : 생성된 session객체의 attribute라는 상태에 new Banana()를 통해 생성된 Banana 객체가 들어간다. 이때 setAttribute의 매개변수가 Object attribute 이므로 heap에는 Banana와 Object가 올라와있고 attribute는 Object를 가리킨다.
  1. Banana ob = (Banana) session.getAttribute(); : attribute가 Object 타입이므로 Banana로 다운캐스팅하여 Banana 타입의 ob 객체에 저장한다.
  1. System.out.println(ob.getName()); : Banana 클래스가 name이라는 상태와 getName()이라는 메서드를 가지고 있으므로 ob.getName()가 사용 가능하며 바나나가 출력된다.
package ex17; class Banana extends Object { private String name = "바나나"; public String getName() { return name; } } class Apple extends Object { private String name = "사과"; public String getName() { return name; } } // 저장소 -> 한개만 띄움 class Session { // singleton으로 하나만 // singleton -> new 제어권 main에게 있음 // 미리 떠 있는 경우 -> 제네릭 불가능 private static Session session = new Session(); private Session() { } public static Session getSession() { return session; } // new 못하니까 <T> 불가능 private Object attribute; // 속성 Property public void setAttribute(Object attribute) { this.attribute = attribute; } public Object getAttribute() { return attribute; } } public class Ob03 { public static void main(String[] args) { Session session = Session.getSession(); // singleton session.setAttribute(new Banana()); //Object ob = session.getAttribute(); // [banana object] Banana ob = (Banana) session.getAttribute(); // 바로 다운캐스팅 System.out.println(ob.getName()); } }
 
 
Share article

jay0628