본문 바로가기

카테고리 없음

자바의 정석 7장 7-2

◆ 다형성 : 여러 가지 형태를 가질 수 있는 능력을 의미한다. 자바에서는 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로 구현함

 

Tv t = new Tv();
CaptionTv c = new CaptionTv();
인스턴스의 타입과 일치하는 타입의 참조변수

 

조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조하도록 하는 것도 가능

Tv t = new CaptionTv();

 

 

 

◈ Tv 타입의 참조변수로는 CaptionTv인스턴스 중에서 Tv클래스의 멤버들만 사용할 수 있다. 

◈ 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.

 

 

CaptionTv c = new Tv();

 

if Caption이 Tv를 상속을 받고 있을 떄에 컴파일 에러가 발생 실제 인스턴스인 Tv의 멤버 개수보다 참조변수 c가 사용할 수 있는 멤버 개수가 더 많기 때문이다.

 

자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은 존재하지 않는 멤버를 사용하고자 할 가능성이 있으므로 허용하지 않는 것이다. 참조변수가 사용할 수 있는 멤버 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다.

 

 

 

자손타입 -> 조상타입 : 형변환 생략가능

자손타입 <- 조상타입: 형변환 생략불가

 

형변환은 참조변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것은 아니기 때문에 참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않는다.

 

 

class Car {
	String color;
    int door;
    
    void drive(){
    	System.out.println("drive,Brrrr~");
    }
    void stop(){
    	System.out.println("stop!!");
    }
    
}

class FireEngine extends Car{
	void water(){
    	System.out.println("water!!!");
    }



}

class CastingTest1{

	public static void main(String args[]){
    	Car car = null;
        FireEngine fe = new FireEngine();
        FireEngine fe2 = null;
        
        fe.water();
        car = fe;
        //car.water(); 컴파일 에러 car 타입으로는 못 부름
        fe2 = (FireEngine)car;// 자손타입 <- 조상타입
        fe2.water();
    }
}

 

 

 

서로 상속관계에 있는 타입간의 형변환은 양방향으로 자유롭게 수행은 가능하지만, 참조변수가 가리키는 인스턴스의 자손타입으로 형변환은 허용되지 않는다.

그래서 참조변수가 가리키는 인스턴스의 타입이 무엇인지 확인하는 것이 중요하다.

 

 

 

 

Instanceof 연산자

(참조변수) instanceof 타입 / 클래스명

참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof 연산자를 사용한다.

 

코드 예시

class InstanceofTest{
	public static void main(String args[]){
    	FireEngine fe = new FireEngine();
        if(fe instanceof FireEngine){
        	System.out.println("This is a FireEngine instance");
        }
        
        if(fe instanceof Car{
        	System.out.println("this is a Car instance");
        }
        
        System.out.println("이름 출력" + fe.getClass().getName());
    
    }

	
}

 

 

출력 결과 

This is a FireEngine instance.
This is a Car instance.
FireEngine

 

 

 

어떤 타입에 대한 instanceof 연산의 결과가 true라는 것은 검사한 타입으로 형변환이 가능하다는 것이다.

 

 

 

class BindingTest3{
	public static void main(String args[]){
    	Parent P = new Child();
        Child C = new Child();
        System.out.println("p.x="+ p.x);
        p.method();
        System.our.println();
        System.our.println("c.x="+c.x);
    	c.method();
    }
}
class Parent{
	int x =100;
    void method(){
    	System.our.println("Parent 함수");
    }
}
class Child extends Parent{
	int x =200;
    void method(){
    	System.out.println("x=" + x);
        System.out.println("super.x=" + super.x);
        System.out.println("this.x+" + this.x);
    }

}
실행 결과
p.x =100
x=200
super.x =100
this.x =200

c.x=200
x=200
super.x=100
this.x=200

 

 

 

자손 클래스 child에 선언된 인스턴스변수 x와 조상 클래스 Parent로 부터 상속받은 인스턴스변수 x를 구분하는데 참조변수 super와 this가 사용된다.

child클래스에서 super.x는 조상 클래스의 parent에 선언된 인스턴스변수를 x를 뜻하면 this.x또는 x는 child클래스의 인스턴스변수 x 뜻함

 

spuer, this 예제

 

class Product{
	int price;
    int bounusPoint
    
    Porduct(int price){
    	this.price = price;
        bonusPoint = (int)(price/10.0);
    }

}

class Tv extends Product{
	Tv() {super(100);}
    public String toString(){
    	return "TV";
    }
}

class Computer extends Product{
	Computer() {super(200);}
    public String toString(){
    	return "Computer";
    }
}
class Audio extends Product{
	Audio() {super(50);}
    public String toString(){
    	return "Audio";
    }
}



class Buyer{
	int money = 1000;
    int bonusPoint =0;
    Product[] item = new Product[10];
    int i=0;
    
    void buy(Product p){
    	if(money < p.price){
        	System.out.println("잔액이 부족해서 물건을 살 수 없습니다.");
            return;
        }
        money -= p.price;
        bonusPoint += p.bonusPoint;
        item[p++] =p;
        System.out.println(p+"을 구입하셨습니다.");
        
    }
    
    void summary(){
    	int sum =0;
        String itemList = "";
        for(int i =0; i < item.length;i++){
        	if(item[i] ==null){break;}
            sum+=item[i].price;
            itemList += item[i] + ", ";
        }
        
        System.out.println("구입하신 물품의 총금액은 " + sum + "만원입니다.");
        System.out.println("구입하신 제품은 " + itemList + "입니다.");
        
    }
}

class PolyArgumentTest2{
	public static void main(String args[]){
    	Buyer b = new Buyer();
        b.buy(new Tv());
        b.buy(new Computer());
        b.buy(new Audio());
        b.summary();
    }

}

 

 

 

추상 클래스

미완성 설계도로 완성된 제품을 만들 수 없듯 추상클래스는 상속을 통해서 자손클래스에 의해서만 완성가능

 

abstract class (클래스명){
	//......
}

 

abstract class Player{
	abstract void play(int pos);
    abstract void stop();
}

class AudioPlayer extends Player{
	void play(int pos){} //추상메서드를 구현해야함 아래도 같음
    void sotp(){}
}

abstract class AbstractPlayer extends Player{
	void play(int pos){} // 추상 메서드 구현
}

 

인터페이스 

인터페이스는 일종의 추상클래스이다. 인터페이스는 추상클래스처럼 추상메서드를 갖지만 추상클래스보다 추상화 정도가 높아서 추상클래스와 달리 몸통을 갖춘 일반 메서드 또는 멤버변수를 구성원으로 못가진다.

 

interface 인터페이스 이름{

	public static final 타입 상수이름 = 값;
    public static abstract 메서드이름(매개변수목록);
}

 

 

class FighterTest{
	public static void main(String[] args){
    	Fighter f = new Fighter();
        
        if(f instanceof Unit){
        	System.out.println("f는 Unit클래스의 자손입니다.");
        }
        if(f instanceof Fightable){
        	System.out.println("f는 Fightable의 인터페이스를 구현");
        }
        if(f instanceof Movable){
        	System.out.println("f는 Movable인터페이스를 구현했습니다.");
        }
        if(f instanceof Attackable){
        	System.out.println("f는 Attackable을 구현했습니다.");
        }
    }
}

class Fighter extends Unit implements Fightable{
	public void move(int x, int y){};//인터페이스 상속 후 구현가능
    public void attack(Unit u){}
}

class Unit{
	int currentHp;
    int x, y;

}
interface Fightable extends Movable,Attackable{}
interface Movable{void move(int x, int y);}
interface Attackable {void attack(Unit u};//인터페이스

 

 

인터페이스의 장점

-개발시간을 단축시킨다

-표준화가 가능하다.

-서로 관계없는 클래스들에게 관계 맺기 가능

-독립적인 프로그래밍이 가능