열심히 끝까지

Java2 day09 본문

Java2(주말)

Java2 day09

노유림 2022. 5. 7. 22:16

[9일차 수업내용]
1. 추상클래스(abstract class)
2. 인터페이스(interface)
===================================================================
1. 추상클래스(abstract class)
            - 클래스를 설계도에 비유한다면, 추상클래스는 미완성 설계도에 비유할 수 있다.
            - 즉, 완성되지 못한 채로 남겨진 설계도를 말한다.
            - 미완성 설계도로는 완성된 제품을 만들 수 없듯이 추상클래스로 인스턴스를 생성할 수 없다.
            - 추상클래스는 상속을 통해서 자손클래스에 의해서만 완성될 수 있다.( 확장, extends )
            - 추상클래스는  새로운 클래스를 작성하는데 있어서 바탕이 되는 클래스로서 중요한 의미를 가진다.

            - 만약, 같은 크기의 Tv라도 기능의 차이에 따라 여러 종류의 모델이 있지만, 
              사실 이들의 설계도는 아마 90% 정도 동일할 것이다.
              서로 다른 여러개의 설계도를 따로 그리는 것 보다 이들의 공통부분만을 미완성 설계도로 만들어 놓고
              이 미완성 설계도를 이용해서 각각의 설계도를 완성하는 것이 훨씬 효율적일 것이다.

            - 추상클래스는 class 키워드 앞에 'abstract' 키워드를 붙이기만 하면 된다.
            - 이렇게 함으로써 이 클래스를 사용할 때 클래스 선언부의 abstract를 보고 이 클래스에는 

              추상메소드가 있으니 상속을 통해서 구현해야 한다는 것을 쉽게 알 수 있다.

                                abstract class 클래스이름 { }

            1-1 ) 추상메소드 ( abstract method )
                        - 선언부만 작성하고 구현부는 작성하지 않는 채로 남겨둔 것이 추상메소드이다.
                        - 즉, 설계를 해놓고 실제 수행될 내용은 작성하지 않았기 때문에 미완성 메소드인 것이다.
                        - 메소드를 이와 같이 미완성 상태로 남겨놓은 이유 메소드의 내용이
                          상속받는 클래스에 따라 달라질 수 있기 때문이다.
                          조상클래스에서 선언부만 작성하고, 실제 내용은 상속받는 클래스에서 구현하도록 비워둔 것이다.
                        - 추상메소드 역시 메소드 앞에 키워드 abstract를 붙여주고 추상메소드는 구현부가 없으므로 

                          괄호{ } 대신 ;(세미콜론)을 붙여준다.

            1-2 ) 추상클래스 작성
                        - 여러 클래스에서 공통적으로 사용될 수 있는 클래스를 바로 작성하기도 하고,
                          기존의 클래스의 공통적인 부분을 뽑아서 추상클래스를 만들어 상속하는 경우도 있다.
                        - 상속이 자손클래스를 만드는데 조상클래스를 사용하는 것이라면,
                          반대로 추상화는 기존의 클래스의 공통부분을 뽑아서 조상클래스를 만드는 것이라고 할 수 있다.

============================================================================
2. 인터페이스( Interface )
            - 인터페이스는 일종의 추상클래스이다.
            - 인터페이스는 추상클래스처럼 추상메소드를 가지지만 추상클래스보다 추상화정도가 높아서
              추상클래스와는 달리 몸통을 가진 일반메소드 또는 멤버변수를 구성원으로 가질 수 없다.
            - 오직 추상메소드와 상수만을 멤버로 가질 수 있으며 그 외의 다른 어떠한 요소도 허용되지 않는다.
            - 추상클래스를 미완성 설계도라고 한다면 인터페이스는 구현된 것이 아무것도 없는 밑그림만 그려져 있는
              기본 설계도라고 할 수 있다.
            - 인터페이스도 추상클래스처럼 불완성한 것이기 때문에 그 자체로 사용되기 보다는 

              다른 클래스를 작성하는데 도움을 줄 목적으로 작성된다.
            
            2-1 ) 인터페이스의 작성
                        - 인터페이스를 작성하는 것 클래스를 작성하는 것이다.
                        - 단, 키워드로 class 대신 interface를 사용한다는 것만 다르다.
                        - 일반적인 클래스의 멤버들과 달리 인터페이스의 멤버들은 다음과 같은 제약사항이 있다.
                                    * 모든 메소드는 public abstract이어야 하며 이를 생략할 수 있다.
                                    * 모든 멤버변수는 public static final 이어야 하며 이를 생략할 수 있다.
                        - 인터페이스에 정의한 모든 멤버에 예외없이 적용되는 사항이기 때문에

                          제어자를 생략할 수 있는 것이며 생략된 제어자는 JVM이 자동적으로 추가해준다.

            2-2 ) 인터페이스의 상속
                        - 인터페이스는 인터페이스로만 상속받을 수 있으며, 클래스와 달리 다중상속,
                          즉 여러개의 인터페이스로부터 상속이 가능하다.

            2-3 ) 인터페이스의 구현
                        - 인터페이스도 추상클래스처럼 그 자체로는 인스턴스를 생성할 수 없으며 추상클래스가 상속을 통해
                          추상메소드를 완성하는 것처럼 인터페이스도 자신에게 정의된 추상메소드의 몸통을 만들어주는
                          클래스를 작성해야 하는데 그 방법은 추상클래스가 자신을 상속받는 클래스를 정의하는 것과 

                          다르지 않다.
                                    * 다만 클래스를 확장한다는 의미의 extends를 사용하는게 아닌
                                      인터페이스를 구현한다는 의미로 implements 키워드를 사용한다.

                        * 인터페이스의 이름은 주로 '~을 할 수 있는' 의 의미로 '~able'로 끝나는 것이 많은데
                          그 이유는 어떠한 기능 또는 행위를 하는데 필요한 메소드를 제공한다는 의미를 강조하기 위해서다.
                        * 또한 그 인터페이스를 구현한 클래스는 '~를 할 수 있는' 능력을 갖추었다는 의미이기도 하다.
                        * 이름이 ~able로 끝나는 것은 인터페이스라고 추측할 순 있지만 

                          반드시 ~able로 끝나는 것은 아니다.

            2-4 ) 인터페이스를 이용한 다형성
                        - 다형성에서 자손클래스의 인스턴스를 조상타입의 참조변수로 참조하는 것이 

                         가능하다는 것을 배웠다.
                        - 인터페이스 역시 이를 구현한 클래스의 조상이라고 할 수 있으므로 해당 인터페이스의 타입의 

                          참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며,

                          인터페이스 타입으로의 형변환도 가능하다.
                        - 즉, 인터페이스는 메소드의 매개변수의 타입으로 사용될 수 있으며, 

                          메소드의 리턴타입으로 인터페이스의 타입을 지정하는 것도 가능하다.
                                    * 리턴타입이 인터페이스라는 것은 메소드가 해당 인터페이스를 구현한 클래스의
                                      인스턴스를 반환한다는 의미이다.

            2-5 ) 인터페이스의 장점
                        ① 표준화가 가능하다.
                                    - 프로젝트에 사용되는 기본 틀을 인터페이스로 작성한 다음, 

                                      개발자에게 인터페이스를 구현하여 프로그램을 작성하도록 함으로써 보다 

                                      일관되고 정형화된 프로그램 개발이 가능하다.
                        ② 개발시간을 단축시킬 수 있다.
                                    - 인터페이스가 작성되면 메소드를 호출하는 쪽과 동시에 다른 한쪽에서는 인터페이스를

                                      구현하는 클래스를 작성하게 되면 인터페이스를 구현하는 클래스가 작성될 때까지 

                                      기다리지 않고 동시에 개발을 진행이 가능하다.
                        ③ 서로 관계없는 클래스에게 관계를 맺어줄 수 있다.
                                    - 서로 상속관계에 있지도 않고, 조상클래스를 가지고 있지 않은 서로 아무런 관계도 없는
                                      클래스에서 하나의 인터페이스를 공통적으로 구현하도록 함으로써 관계를 맺어줄 수 있다.
                        ④ 독립적인 프로그래밍이 가능하다.
                                    - 인터페이스를 이용하면 클래스의 선언과 구현을 분리시킬 수 있기 때문에 실제 구현에
                                      독립적인 프로그램을 작성하는 것이 가능하다.
                                    - 클래스와 클래스간의 직접적인 관계를 인터페이스를 이용해서 간접적으로 

                                      관계를 변경하면 한 클래스의 변경이 다른 클래스에 영향을 미치지 않는 

                                      독립적인 프로그래밍이 가능하다.


==========================================================================
package example01;

public abstract class GameUnit {
            int x, y; // 유닛의 위치(좌표)
            abstract void move(int x, int y); // 이동하는 메소드 : 추상메소드
            void stop() {/* 현재 위치에 정지 */}
            /*
             *  추상클래스는 추상메소드도 가질 수 있다.
             *    ==> 추상메소드만 가지는 것이 아닌 일반 메소드도 얼마든지 선언이 가능하다.
             */
}
class Marine extends GameUnit{// 보병
            @Override
            void move(int x, int y) {
                        System.out.println("Marine 이동");
            }
            void stimPack() {/* marine이 가지고 있는 고유한 기술 */}
}
class Tank extends GameUnit{// 전차
            @Override
            void move(int x, int y) {
                        System.out.println("Tank 이동");
            }
            void changeMode() {/* Tank가 가지고 있는 고유한 기술 */}
}

class DropShip extends GameUnit{ // 날아다니는 수송선
            @Override
            void move(int x, int y) {
                        System.out.println("DropShip 이동");
            }
            void load() {/* DropShip이 가지고 있는 고유한 기술*/}
            void unload() {/* DropShip이 가지고 있는 고유한 기술*/}
}

----------------------------------------------------------------------------------
package example01;

public class GameUnitEx {
            public static void main(String[] args) {
                        // GameUnit gu = new GameUnit();
                        // ==>추상클래스는 미완성 메소드를 가질 수 있기 때문에 객체 생성 불가
                        Marine m1 = new Marine();
                        Marine m2 = new Marine();
                        Tank t = new Tank();
                        DropShip d = new DropShip();
                        m1.move(1, 1);
                        m2.move(1, 1);
                        t.move(1, 1);
                        d.move(1, 1);

                        // 다형성 + 배열
                        GameUnit[] units = new GameUnit[4];
                        units[0] = new Marine();
                        units[1] = new Tank();
                        units[2] = new Marine();
                        units[3] = new DropShip();

                        for(int i = 0; i < units.length; i++) {
                                    if(units[i] == null) {
                                                break;
                                    }
                                    units[i].move(1, 1);
                        }
                        // 위의 방법처럼 marineGroup을 만들어 Marine객체 20개 생성
                        // 생성 완료했다면 전부 3, 8좌표로 이동

                        // 1번째 방법
                        Marine[] marineGroup = new Marine[20];
                        for(int i = 0; i < marineGroup.length; i++) {
                                    marineGroup[i] = new Marine();
                                    marineGroup[i].move(3, 8);
                        }
                        System.out.println();
                        // marineGroup[10] = new Tank();

                        // 2번째 방법
                        GameUnit[] marineGroup2 = new GameUnit[20];
                        for(int i = 0; i < marineGroup2.length; i++) {
                                    marineGroup2[i] = new Marine();
                                    marineGroup2[i].move(3, 8);
                        }
                        marineGroup2[10] = new Tank();
                        /*
                         *  위 코드는 공통조상인 GameUnit클래스 타입의 참조변수 배열을 통해서
                         *  서로 다른 종류의 인스턴스를 하나의 묶음으로 다룰 수 있다는 것을 보여주기 위함이다.
                        *  다형성에서 배웠듯이 조상클래스의 참조변수로 자손클래스의 인스턴스를 참조하는 것이 

                        * 가능하기 때문에 이처럼 조상클래스 타입의 배열에 

                        * 자손클래스의 인스턴스(객체)를 담을 수 있는 것이다.
                         *  여기서 호출되는 move(3, 8)은 자손클래스에서 재정의된 메소드가 호출되는 것이다.
                         */

                        //참조변수의 형변환
                        GameUnit marine1 = new Marine();
                        Marine marine2 = (Marine)marine1;
                        System.out.println(marine1);
                        System.out.println(marine2);

                        System.out.println();

                        Object[] units3 = new Object[3];
                        units3[0] = new Marine();
                        units3[1] = new Marine();
                        units3[2] = new Marine();
                        for(int i = 0; i < units3.length; i++) {
                                    // units3[i].move(1 ,1); Object에는 move라는 메소드가 없어서 호출 불가
                        }
            }
}
--------------------------------------------------------------------------------------------------------------------
package example02;

public class InterfaceEx01 {
            public static void main(String[] args) {
                        A a = new A();
                        a.method(new B()); // InterfacePractice i = new B();
                        a.method(new C()); // InterfacePractice i = new C();
            }
}
interface InterfacePractice{
            void method();
}

class A{
            public void methodA(B b){
                        B b1 = new B();
                        b1.method();
                        // b1.methodB는 B의 구현부가 만들어지지않았기에 
                        //빨간줄에 밑줄이 그어짐(만들어질 때까지 기다리는 중...)
             }

            public void method(InterfacePractice i) {
                        i.method();
                        // 아직 만들어지지않아도 독립적으로 작용해 빨간줄이 그어지지 않는다.
            }
}
class B implements InterfacePractice{
            @Override
            public void method() {
                        System.out.println("method() int B Class");
            }
}
class C implements InterfacePractice{
            @Override
            public void method() {
                        System.out.println("method() int C Class");
            }
}
------------------------------------------------------------------------------------
package example02;

public class InterfaceEx02 {
            public static void main(String[] args) {
                        A1 a = new A1();
                        a.method();
            }
}
interface I1{
            void method();
}

class B1 implements I1{
            @Override
            public void method() {
                        System.out.println("methodB() in B Class");
            }
            @Override
            public String toString() { return "Class B1"; }
}
class C1 implements I1{
            @Override
            public void method() {
                        System.out.println("methodC() in C Class");
            }
            @Override
            public String toString() { return "Class C1"; }
}
class InstanceManager{
            public static I1 getInstance() {
                        // return new B1();
                        return new C1();
            }
}
class A1{
            void method() {
                        I1 i = InstanceManager.getInstance();
                        i.method();
                        System.out.println(i.toString());
            }
}
---------------------------------------------------------------------------------
package example03;

public class RepairableEx {
            public static void main(String[] args) {
                        Tank  tank = new Tank();
                        DropShip dropship = new DropShip();
                        Marine marine = new Marine();

                        SCV scv= new SCV();

                        // STEP1 : repair(Object u)일 때
                        /*
                        scv.repair(tank);
                        scv.repair(dropship);
                        scv.repair(marine);
                        */

                        // STEP2 : repair(Repairable r)일 때
                        scv.repair(tank); // Repairable r = new Tank();
                        scv.repair(dropship); // Repairable r = new DropShip();
                        // scv.repair(marine); // Repairable r = new Marine();
            }
}
interface Repairable{}

class Unit{
            int hitPoint;
            final int MAX_HP; // 상수지만 처음에 초기화하지 않고 생성자에서 초기화 가능
            public Unit(int hp) {
                        this.MAX_HP = hp;
            }
}
class GroundUnit extends Unit{ // 지상으로만 다니는 유닛
            public GroundUnit(int hp) {super(hp);}
            // 자식클래스에서 부모생성자를 무조건 호출해야 함.
}
class AirUnit extends Unit{ // 공중으로만 다니는 유닛
            public AirUnit(int hp) {super(hp);}
}
class Tank extends GroundUnit implements Repairable{
            public Tank() {
                        super(150);
                        hitPoint = MAX_HP;
            }
}
class Marine extends GroundUnit{
            public Marine() {
                        super(40);
                        hitPoint = MAX_HP;
            }
}
class DropShip extends AirUnit implements Repairable{
            public DropShip() {
                        super(125);
                        hitPoint = MAX_HP;
            }
}
class SCV extends GroundUnit implements Repairable{
            public SCV() {
                        super(60);
                        hitPoint = MAX_HP;
            }

            // STEP1 : repair()의 매개변수가 Object 타입일 때
            /*
            void repair(Object obj) {
                        if(obj instanceof Unit) {
                                    Unit u1 = (Unit)obj;
                                    while(u1.hitPoint != u1.MAX_HP) {
                                                u1.hitPoint++;
                                    }
                                    System.out.println("수리 완료!");
                      }
            }
            */

            // STEP2 : repair()의 매개변수가 Repairable 타입일 때
            void repair(Repairable r) {
                        if(r instanceof Unit) {
                                    Unit u1 = (Unit)r;
                                    while(u1.hitPoint != u1.MAX_HP) {
                                                u1.hitPoint++;
                                    }
                                    System.out.println("수리 완료!");
                        }
            }
}

'Java2(주말)' 카테고리의 다른 글

Java2 day13  (0) 2022.05.21
Java2 day12  (0) 2022.05.15
Java2 day11 보충  (0) 2022.05.15
Java2 day11  (0) 2022.05.14
Java2 day10  (0) 2022.05.09