간단한 게임 케릭터 공격 프로젝트로 총정리 해보자.

1. 부모클래스가 될 Hero 클래스를 만들어주자. 게임의 케릭터를 담당할 것이다.

  • 이름변수, 공격메소드 + 생성자를 생성해준다.

2. 각 직업인 Warrior, Archer, Wizard 클래스를 만들어준다.

  • 부모클래스에서 받은 필드(name)를 초기화할 생성자를 만들어준다.
  • 각 직업에 해당하는 기술메소드()를 만들고, 출력문에 해당 기술명을 적어주자.

public class Warrior extends Hero {

    public Warrior(String name) {
         super(name);
        
     }
    
     public void groundCutting() {
         System.out.println("대지 가르기!");
     }

}

3.  메인클래스-메인함수를 만들고 구현해보자.

  1. 먼저 각 직업 클래스들을 담을 Hero클래스의 변수 배열을 만들자(다형성 개념으로, 부모클래스에는 자식클래스 어느것이든 담긴다)
    Hero[] heros = new Hero[3];
  2. 배열의 각 원소에다가 해당클래스 객체를 생성해서 참조해주자.
    heros[0] = new Warrior("전사");
    heros[1] = new Archer("궁수");
    heros[2] = new Warrior("마법사");

  3. for문안에 각 원소들에서 attack()메소드를 사용하도록 해보자. 공통적인 부모클래스의 메소드이므로, for문에서 한번에 해결할 수 있다.
  4. 바로 밑에서는 if문 + < instanceof > 를 이용하여, 부모클래스인 Hero변수안에 있지만, 각 직업클래스의 객체인지 판별하여 –> 
    해당 객체가 맞다면, 그 클래스의 참조변수 temp안에다가 형변환을 해서 넣은 뒤-> 특수기술메소드()를 사용할 수 있게 호출해주자.
    *형변환을 하는 이유는 지금 객체가 <부모클래스인 Hero의 참조변수 의 배열 >속에 있기 때문이다.
    for(int i = 0; i< heros.length; i++) {
       
        heros[i].attack();
       
        if(heros[i] instanceof Warrior) {
            Warrior temp = (Warrior) heros[i];
            temp.groundCutting();
        }
       
    }
  5. 마찬가지로 다른 직업도 같은 작업을 해주고, 출력해보자.

자바 (2)

Obejct클래스는 모든 객체의 조상인 부모클래스이다. 모든 객체가 암시적으로 extends Object를 가지고 있다고 생각하면 된다.

이런 계층구조를 가지는 이유는, 객체를 만들어내는 모든 클래스가 공통으로 포함하고 있어야 하는 기능을 제공하기 위해서이다.


Object가 모든 클래스의 부모이며, 다형성의 개념으로서, 어느 객체든 변수에 받을 수 있을 뿐만 아니라
어느클래스든 자식클래스가 되므로 (형변환)하여 대입할 수 있다는 개념을 이용해서
비교시 하는 메소드()의 파라미터에 해당 객체형이 아닌 Obejct형으로 받아서, 그것을 형변한 해도 비교가 가능하다는 것을 실습해보자.


1. 같은 클래스지만, 다른 필드값을 가질 객체를 생성할 Archer클래스를 만들어보자.

이름과, 공격력 변수를 만들고, 생성자도 만들어, 사용되는 곳에서 초기화 될 수 있게 하자

2. 다형성을 이용해서, 부모클래스의 참조변수 Object안에 들어있는 객체가 Archer객체로서, 필드값(name, power) 같는지 물어보고
   boolean을 반환하는 equals()메소드를 만들어주자.
  ( 비교할 때, 둘 다 Archer객체를 만들어서 비교할 것임)

확인하는 방법은

  1. 들어오는 Object변수를, 확인해줄 Archer클래스로 형변환 한다. 그러면 Object변수는 Archer의 객체가 된다.
    그러면 자동으로, Archer의 필드인 name과 power를 가지고 있게 된다.
    ( 상위계층(Object) –> 하위계층(Archer)에 대입할 때는 형변환 필수)
  2. Object에서 비롯하여 형변환이 된 Archer객체를 –> Archer의 참조변수 temp 에 넣는다.
    이렇게 object변수를 Archer형으로 형변환할 수 있는 이유는 <더 상위계층, 부모클래스>이기 때문에 가능하다.
    넘어온 매개변수 Object는 암시적으로 자신을 상속한 Archer클래스로 형변환이 가능하다.
  3. if문으로 Archer객체가 가지는 name과 < – > temp속 name이 같은지 물어본다. 동시에 power도 같은지 물어본다.
  4. 같으면 true, 그렇지 않다면 false를 return한다.


public class Archer {
    
     String name;
     String power;
    
     public Archer(String name, String power) {
         this.name = name;
         this.power = power;
        
     }
    
    
     public boolean equals(Object obj) {
         Archer temp = (Archer) obj;
         if( name == temp.name && power == temp.power) {           
            
             return true;   
            
         }else {
            
             return false;
            
         }
        
     }
    
    
}


3. 메인클래스-메인함수를 구현해서, Archer클래스의 객체를 생성하고, 사용해보자.

  1. 2개의 객체를 생성하고, 참조변수에 넣어준다.
  2. 이제 2개의 객체를 비교해주기 위해서,  출력문에 archer1 == archer2 객체를 바로 비교한다. 당연히 false가 출력된다.
    필드값들이 서로 다르게 초기화 되었기 때문에 서로 다른 객체이다.
  3. 만약 초기화값을 같게 해줘도, false가 반환된다. 기본적으로 각각 생성된 객체는 hashCode가 다르기 때문에,
    초기화하여 내부 필드들이 갔더라도 완전이 다른 객체이다.
  4. 이제 Archer클래스의 equals()메소드를 이용해서 비교해보자. 초기화 값이 같다면 true가 반환된다.
    equals()라는 메소드는 같은 Archer객체로서, 들어있는 필드값이 같은지를 물어보기 때문이다.

public class Main {

    public static void main(String[] args) {
        
         Archer archer1 = new Archer("궁수1", "상");
         Archer archer2 = new Archer("궁수1", "상");
         System.out.println(archer1 == archer2);
         System.out.println(archer1.equals(archer2));

    }

}

정리

모든 클래스의 조상은 Object클래스이다. 같은 종류의 객체를 비교하는 메소드를 만들 때,  파라미터에 (Object obj)가 들어있어도
호출시에는 자식이되는 모든 클래스들을 넣어도 상관없다. 대신 형변환을 해줘야 같은 객체의 필드값들이 같은지 비교할 수 있다.

다형성이란 객체를 사용할 때, 변수형태를 바꾸어서 여러타입의 객체를 참조할 수 있다.

결과적으로 이러한 다형성의 개념을 적절하게 이용할 때, 소스코드를 유연하게 구성할 수 있다.

다형성은 <부모 클래스 타입의 참조변수><하위 클래스의 객체를 참조할 수 있게 해준다.>

과일 정보 프로젝트를 구현해보자

1. 부모클래스가 될 Fruit 클래스를 생성하자

  1. 과일의 이름과 가격 , 신선도 변수를 선언해주자.
  2. 변수를 출력할 메소드도 만들어주자.

public class Fruit {

    String name;
     int price;
     int fresh;
    
     public void show() {
         System.out.println("이름 : " + name);
         System.out.println("가격 : " + price);
     }
    
}

2. 자식 클래스가 될 Peach클래스를 생성하자.

  1. 부모클래스의 필드를 초기화 해주자
  2. 만약, 부모클래스안에 부모필드를 초기화하는 생성자가 있다면,  자식클래스에서는 super.( , , ); 초기화 하는 것이 맞다.

public class Peach extends Fruit {
    
     public Peach() {
        
         price=1500;
         name = "복숭아";
         fresh = 75;
        
     }

}

3. 메인클래스와 메인함수를 생성한다.

4. 이제 다형성의 개념으로서, 부모클래스의 참조변수 에다가, 자식클래스의 객체를 생성해서 넣어준다.
   비록 부모클래스의 변수에 넣었지만, 필드나 메소드가 작동하는 것은 new뒤에 붙은 자식클래스의 객체가 본질이다.
   show()메소드는 부모클래스에만 존재하기 때문에, 부모의 메소드를 호출한다. 필요하다면 오버라이딩해서 쓰면된다.

public class Main {

    public static void main(String[] args) {
        
         Fruit fruit = new Peach();
         fruit.show();

    }

}

5. 또하나의 자식클래스 Banana를 만들고, 부모 필드를 초기화해주자.

public class Banana extends Fruit {
    
     public Banana() {
     price = 1000;
     name = "바나나";
     fresh = 80;
     }

}

6. 메인 클래스의 메인함수에서 다시 부모클래스 참조변수에 Banana객체를 넣자.

Fruit fruit2 = new Banana();
fruit2.show();


실제로 어떤 것을 구현할 때, 복숭아인지 / 바나나인지 사용자가 임의적으로 선택할 수 있게 한다.

자식클래스의 인스턴스(객체)를   자신의 변수에다가 넣는 것이 바로 다형성이다.


7. 실제적으로 사용되는 코드로 만들어보자.

  1. 스캐너를 생성한 뒤, 복숭아/ 바나나를 숫자로 입력받도록하자.(입력받을땐 println이 아니라 print로 출력)
  2. 담을 변수는 미리 선언해놓고, 넣을 객체만 if문에 따라 new Banana인지 new Peach인지 구분해서 넣어준다.

public class Main {

    public static void main(String[] args) {
        
        
         Scanner sc = new Scanner(System.in);
         System.out.print("복숭아는 1, 바나나는 2를 입력하세요 : ");
         int input = sc.nextInt();
        
         Fruit fruit;
         if(input == 1) {
             fruit = new Peach();
             fruit.show();
         }else if(input ==2) {
             fruit = new Banana();
             fruit.show();
         }
        
     }

}

다형성 정리

다형성을 이용해서 만약 과일이 아니라, 게임케릭터라고 생각해보면, 입력받은 숫자대로 전사/마법사 등등을 선택 할 수 있다

미리 부모클래스를 만들어, 을 만들어놓은 다음

사용자가 입력한 값에 따라서, 선택된 그리고 완전히 다른 인스턴스 (객체)를 쉽게 바꿔서 대입할 수 있다는 측면에서

유동적으로 자기자신의 변수에 불러오므로, 내용도 유동적으로 바꿔줄 수 있다.

자바














추상클래스와 비슷한 개념으로서,  추상같은 경우, 어느 클래스안에 어느 메소드가 사용될지 암시해주는 설계제공이었다.
인터페이스 같은 경우, 더 선호되는 설계기능으로, 비록 같은 추상메소드를 가지고 있으나, 차이점은

  1. 다중 상속이 가능하게 한다.
  2. 사전에 정의된 상수만 가질 수 있다.(추상클래스는 필드를 여러개 가질 수 있고, 상수가 아니어도 된다)
  3. 사전에 정의된 추상메소드만 가질 수 있다.(추상클래스는 추상메소드 이외에 다른 일반 메소드들도 가질 수 있다. 따라서 일반메소드의 body{}도 채워놓을 수 있다.)
    추상클래스보다 요구되는 설계의 기준이 더 높아서 더 체계적이라 할 수 있다.


[1] 인터페이스를 선언하고 메소드를 다루어보자.


1. Dog라는 인터페이스를 만들고, abstract로 추상메소드를 만들자(추상메소드를 만드는 과정은 추상클래스와 동일하다)
public interface Dog {
    
     abstract void crying();
     public void show();
    

}


2. 메인클래스, 메인함수를 만들어서 인터페이스를 상속(implements)해서 구현화해보자.  반드시 구현화할 메소드들을 오버라이딩해야한다.

public class Main implements Dog{

   
     public static void main(String[] args) {

    }

    @Override
     public void crying() {
         // TODO Auto-generated method stub
        
     }

    @Override
     public void show() {
         // TODO Auto-generated method stub
        
     }

}

3. 재정의로 구현화할 메소드 내용을 채워준다.

4. 메인클래스에서 상속했으니, static인 메인메소드에서 사용하려면, Main 객체를 만들어서 사용해야한다.

public class Main implements Dog{

   
     public static void main(String[] args) {
        
         Main main = new Main();
         main.crying();
         main.show();
        

    }

    @Override
     public void crying() {
        
         System.out.println("월 월!");
        
     }

    @Override
     public void show() {
         System.out.println("Hello World!");
     }

}


[2] 인터페이스의 다중 상속을 학습해보자.


  -  2개의 추상클래스를 만들어서, 2개를 모두 상속하면 오류가 난다.

1. Dog 인터페이스를 복사해서 , Cat 인터페이스를 만들어보자.

2. 메인클래스에서 implements Dog, Cat 으로 2개의 인터페이스를 다중상속해보자.

3. 반드시 오버라이딩해야하는 인터페이스의 메소드들을 추가해보자.
   만약 2개의 인터페이스의 추상메소드가 겹칠 경우, 하나만 구현화해줘도 문제가 없다.

public class Main implements Dog, Cat{

   
     public static void main(String[] args) {
        
         Main main = new Main();
         main.crying();
         
         main.dog();
         main.cat();
        

    }

    @Override
     public void crying() {
        
         System.out.println("월 월!");
        
     }


     @Override
     public void cat() {
         System.out.println("고양이 입니다.");
     }

    @Override
     public void dog() {
         System.out.println("개 입니다.");
        
     }

}

절대로 변하지 않는 특정한 것을 사용할 때 final을 사용한다. 변수 메소드 클래스에 모두 사용할 수 있다.
변수의 경우, 변하지 않는 상수
메소드의 경우,  상속한 자식클래스에서 오버라이딩(재정의)가 불가능한 메소드
클래스의 경우, 상속이 불가능한 완전한 클래스가 된다.

[정리] final을 사용해서, 더이상 바뀌지 않게 보호하는 기능을 한다고 할 수 있다.

cf) 인터페이스의 필드는 모두 final이 생략되어있다.

[1] final 상수를 만들어보자.

1. final 변수(상수)를 만들어보자. 이 때, 어떠한 수를 다시 대입하면 오류가 난다.

        final int number = 10;
         System.out.println(number);
        
         number=5; //오류가 난다.

[2] final 메소드를 만들어보자.(1 ~3 까지는 final이 아니라서, 재정의된 오버라이딩 메소드를 호출한다.)

1. Parent클래스를 만들고, 출력문을 가진 메소드 show()를 만들자.

public class Parent {

    public void show() {
        
         System.out.println("Hi from Parent");
        
     }   
    
}

2. 메인클래스에서 Parent 클래스를 상속받고, (STATIC 메인메소드에서도, 재정의한 메소드를 호출할 수 있게)메인클래스 객체를 생성해서,
   부모parent에 있는 show()메소드를 이용해보자.

public class Main extends Parent{
     public static void main(String[] args) {
         Main main = new Main();
         main.show();       
     }
}

3. 이제 부모클래스의 show()를 오버라이딩 해서, 재정의한 show();가 호출되도록 해보자.

public class Main extends Parent{

    @Override
     public void show() {
         System.out.println("hello from Main");
     }

    public static void main(String[] args) {

        Main main = new Main();
         main.show();
        
     }
}

4. 이제 Parent 클래스의 show()메소드에 final을 붙혀보자. -> 자식인 Main클래스에서 오버라이딩 한 곳에서 오류가 난다.
   ***final이 붙은 메소드는, 부모클래스의 것이더라도 자식이 오버라이딩해서 사용 못한다!

    public final void show() {       
         System.out.println("Hi from Parent");       
     }
    


[3] 클래스에 final을 붙혀보자.

1. Parent클래스에 public을 빼고, final class로 바꿔보자.  => 상속한 자식인 Main클래스에서 오류가 난다.
  *** final이 붙은 클래스는, 다른 클래스에서 더이상 상속하지 못한다.

MY) 완전한 설계도이므로, 객체를 생성해서 사용할 수 밖에 없다. 상속할 자식은 더이상 존재할 수 없음.

[정리] final을 사용해서, 더이상 바뀌지 않게 보호하는 기능을 한다고 할 수 있다.

+ Recent posts