모든 클래스의 부모 (생략되어 있다.)
1. Object 타입 일치
- 객체 instanceof 클래스 : 객체가 특정 클래스의 인스턴스인지 확인해서 true/false 리턴
- 모든 클래스는 Object 클래스를 상속받기 때문에 특정 클래스를 new하게 되면 특정 클래스와 Object 클래스 둘 다 메모리에 뜨게 된다.
- callName(new Dog()) : callName의 매개변수가 Object u이므로 Object u = new Dog()와 같다. 메모리 heap에 Dog와 Object가 둘 다 떠있고 u는 그 중 Object를 가리키고 있는 상황이다.
- u instanceof Dog: Dog 클래스가 Object 클래스를 상속 받고 있고 즉, 메모리에 Dog가 떠있기 때문에 true를 반환한다.
- Dog d = (Dog) u; : Object 클래스의 객체 u를 Dog타입으로 다운캐스팅 할 수 있다.
- 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());
}
}

2. Generic 기본기
new 할 때 타입을 결정할 수 있다.
- Generic이란 클래스 내부에서 타입이 지정되지 않고 외부에서 사용자에 의해 결정되는 기법이다.
- 제네릭에는 클래스 타입만 지정 가능하다.
- 제네릭은 new할 때 타입을 결정할 수 있는데 지정하지 않을 경우 디폴트값으로 Object 타입이 된다.
- 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 저장 방식의 차이가 무엇일까?
- Object와 제네릭 둘 다 기본 자료형을 저장하는 것은 동일하다. (무엇이든 사용 가능하다.)
- Object와 Generic를 각각 사용해야하는 경우가 다르다
- Object를 쓸 수 밖에 없는 경우
- 제네릭을 써야 되는 경우 ( 제네릭으로 저장하는 것이 유리하다.)
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 타입으로 저장한다.
- new를 할 수 없어서 제네릭을 사용할 수 없는 경우 Object를 사용해야한다.
- private static Session session = new Session(); : Session을 다른 클래스 등에서 new 못하지만 단 한 번 메모리 상에 올려놓아야 할 때, private과 static으로 설정하여 해당 클래스 내부에서 new를 하면 단 한 개의 객체가 main 실행 전 생성되고 main에서 new 제어권을 가지게 된다. ⇒ 싱글톤
- 싱글톤 패턴 → 기본 생성자: private / 메소드: static
- Session session = Session.getSession(); : getSession() 메서드를 통해 session 싱글톤 객체를 가져온다.
- session.setAttribute(new Banana()); : 생성된 session객체의 attribute라는 상태에 new Banana()를 통해 생성된 Banana 객체가 들어간다. 이때 setAttribute의 매개변수가 Object attribute 이므로 heap에는 Banana와 Object가 올라와있고 attribute는 Object를 가리킨다.
- Banana ob = (Banana) session.getAttribute(); : attribute가 Object 타입이므로 Banana로 다운캐스팅하여 Banana 타입의 ob 객체에 저장한다.
- 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