2014. 11. 9. 17:00ㆍBasic/etc
1. 다형성(polymorphism)
다형성이란 상위 클래스의 타입의 참조변수로 하위클래스의 객체를 참조하는 것을 말한다.
※ 추상클래스나 인터페이스도 상위클래스에 해당한다. 추상클래스를 상속한 클래스의 객체나 인터페이스를 구현한 클래스의 객체를 상위 추상클래스 형이나 인터페이스 형 변수로 참조 가능하다. 추상클래스와 인터페이스는 뒤에 단원에서 설명한다.
class Person
{
int x;
}
class Student extends Person
{
int y;
}
Person p = new Student(); // 다형성
// p가 참조할 수 있는 멤버는 Student가 다 가지고 있기에 문제가 없다.
Student s = new Person(); // X compile error!
// s가 참조할 수 있는 멤버가 Person보다 많기에 에러 발생
2. 참조변수의 형변환
(데이터타입) : 형변환 연산자
ex) (String)s, .....
※ 형변환 연산자는 기본타입에서도 사용가능하다. ex) (int)2.3
모든 참조변수는 변수에 객체의 참조값이 들어오기에 자식타입(서브타입)으로 강제 형변환이 가능하다. 강제형변환을 하여도 변수가 변한 것이지 객체자체가 변화한 것이 아니므로 실행시 예외가 발생할 수도 있다.
Person p = new Person();
Student s = (Student)p; // p변수를 강제로 Student로 형변환 , 정상적으로 컴파일
s.y = 10;
// 실행시 에러! , s가 가르키는 실체 객체는 new Person()으로 만들어 졌기때문에 y가 없다.
3. 매개변수의 다형성
class Car
{
void powerOn(){}
}
class Spark extends Car
{
void powerOn()
{
System.out.println("스파크 시동켜다");
}
}
class Morning extends Car
{
void powerOn()
{
System.out.println("모닝 시동켜다");
}
}
class Ray extends Car
{
void powerOn()
{
System.out.println("레이 시동켜다");
}
}
운전자는 위의 차들을 움직이기 위해서는 오버로딩된 메서드 3개가 필요하다.
class Driver
{
void carMove(Spark spark)
{
spark.powerOn();
}
void carMove(Morning morning)
{
morning .powerOn();
}
void carMove(Ray ray)
{
ray.powerOn();
}
}
하지만 다형성을 이용하면 다음과 같이 하나의 메서드로 가능하다.class Driver
{
void carMove(Car car)
{
car.powerOn();
}
}
Car형 car변수는 Spark, Morning, Ray 객체를 다 참조 가능하다.
다형성을 활용하면 새로운 하위클래스 형식을 프로그램에 추가 하더라도 코드를 굳이 바꿀 필요가 없다.
객체 instanceof 클래스이름 : 객체가(객체변수, 참조변수) 뒤의 클래스형으로 참조가 가능하면 true 불가능하면 false
Car c = new Car();
Ray r= new Ray();
if( c instanceof Ray){....} // if조건 false
if( r instanceof Car){....} // if조건 true
※ 메모리 생성 형태로 다형성 이해하기
다형성을 의미론적으로 설명하면 위의 예에서 "학생은 사람이다"와 "사람은 학생이다"에서 문맥상 앞의 문장이 맞는 표현이다.(부모형에 자식형이 들어가는게 맞는 표현이다)
그럼 메모리에 생성되는 형태로는 어떻게 설명되는가?
다음과 같은 상속관계의 클래스가 있다.
class A{
int x;
int y;
void testOne(){}
void testTwo(){}
}
class B extends A{
int y;
int z;
void testTwo(){}
void testThree(){}
}
new A() 실행시 메모리에 참조될 수 있는 A객체의 멤버는 다음과 같다.(최상위 클래스인 Object클래스의 멤버는 당연히 참조되지만 이 설명에서는 생략한다 )
this.x
this.y
this.testOne()
this.testTwo()
new B() 실행시 메모리 참조될 수 있는 B객체의 멤버는 다음과 같다.(하위클래스의 생성자에서 상위 클래스의 생성자를 호출한다고 생성자 부분에서 설명했다. 그 의미를 알고 있다면 아래의 결과가 이해하기 쉬울것이다)
super.x
super.y
super.testOne()
super.testTwo()
this.y
this.z
this.testTwo()
this.testThree()
B클래스의 내부에서는 super, this키워드를 이용하여 동일한 x변수나 testTwo()메서드를 구분하여 호출이 가능하겠지만, B클래스 아닌 다음과 같은 예에서는 B클래스의 객체에 대해서 super나 this같은 키워드 사용이 불가하다.
class C{
B b = new B();
// b.super.testTwo(); //이런 형태의 호출은 자바 문법에서는 제공하지 않는다.
// b.this.testTwo(); //이런 형태의 호출은 자바 문법에서는 제공하지 않는다.
// 만약에 위와 같은 형태의 표현을 자바언어가 지원했다면 자바언어가 좀더 유연해졌을까?
// 아님 그냥 좀더 배우기 힘든 언어가 되었을까?
b.testTwo();
}
그럼 위의 testTwo()는 둘중 어떤 메서드를 호출하는가? 자바언어는 다른클래스에서 객체를 생성후 멤버메서드 호출시 this.메서드가 있으면 this.메서드를 호출하고, 없을때만 super.메서드를 호출한다. -> 오버라이딩
A a = new B(); 되는 이유는 a변수가 참조할 수 있는 x, y, testOne(), testTwo()를 new B()객체(super.x, super.y, this,y, super.testOne(), super.testTwo()(존재는 하지만 오버라이딩때문에 외부에서는 호출불가능) ,this.testTwo())가 다 가지고 있기에 가능한 것이고(다형성), B b = new A();의 경우는 b변수가 참조할 수 있는 x, y, z, testOne(), testTwo(), testThree()를 new A()객체가 다 가지고 있지 않기때문에 오류가 발생한다.
class A{
int x = 1;
void test(){System.out.println("A실행")}
}
class B extends A{
int x = 2;
void test(){System.out.println("B실행")};
}
class MainTest{
public static void main(String[] args){
A a = new B();
System.out.println(a.x); // 1출력 - 변수는 오버라이딩이 안된다. 부모참조변수로 호출하면 super.x가 호출된다.
// 변수는 오버라이딩 대상이 아니니 부모와 동일한 이름의 변수는 사용금지!
a.test(); // "B실행"출력 - 메서드 오버라이딩에 의해 super.test()가 아닌 this.test()가 호출된다.
}
- 부모와 동일한 이름의 변수이름을 사용하는 것 -> 잘못된 습관 - 부모와 동일한 이름의 메서드 이름을 사용하는 것(구현기능이 유사할때) -> 잘된 습관(객체지향의 오버라이딩을 사용하는 습관) |
'Basic > etc' 카테고리의 다른 글
추상클래스와 인터페이스 (0) | 2014.11.09 |
---|---|
접근제어자와 캡슐화 (0) | 2014.11.09 |
객체지향? 추상화? (0) | 2014.11.09 |
포함? 상속? (0) | 2014.11.09 |
오버로드? 오버라이딩? (0) | 2014.11.06 |