추상메서드 추상클래스 - accidentlywoo/java GitHub Wiki

추상메서드 추상클래스


  • 추상 메서드와 추상 클래스의 개념을 이해하고, 내부 클래스의 종류를 구분하여 활용 방법을 설명할 수 있다.
  • 클래스의 형변환 개념을 이해하고, 다형성을 이용한 프로그램을 구현할 수 있다.

추상 클래스와 내부 클래스

abstract : 메서드와 클래스를 선언할 때 사용하는 Modifier. 메서드를 선언할 때 abstract예약어를 붙이면 현재 메서드를 추상 메서드로 정의하겠다는 의미임.

추상 메서드란 무엇일까?

  • 메서드의 시그니처(리턴타입, 메서드명, 매개변수)만 정의
  • 구체적인 행위, 즉 블록({})부분은 정의되지 않은 특수한 메서드

abstract returnType name([argType argName,...])

메서드의 시그니쳐가 선언되어 있기 때문에 메서드의 기본적인 기능 유추 가능

  • 객체지향 프로그램에서 추상 메서드가 필요한 이유는? 의미가 없는 추상 메서드 --자식 클래스에서 Overriding 했을 때--> 상속을 통해 의미를 가지는 추상 메서드

추상 메서드는 상속과 관련이 깊은 예약어임.

추상 클래스

클래스 선언부에 abstract를 사용하면, 해당 클래스를 추상 클래스로 선언한다는 의미가 됨. 추상 클래스는 일반적으로 하나 이상의 추상 메서드를 포함함. -> 추상 메서드가 없는 클래스도 추상 클래스로 선언 가능 추상 메서드를 포함하고 있는 클래스는 추상 클래스로 선언되어야 함

  • 추상 클래스는 객체 생성을 할 수 없음 추상 메서드를 포함하고 있는 추상 클래스를 객체로 생성할 수 있도록 허용하면, 객체 참조 변수를 통해서 추상 메서드를 호출할 수 있게 됨. -> 아무런 기능도 제공하지 않는 추상 메서드가 호출된다는 것은 논리적으로 맞지 않음 문법적으로 객체 생성을 허용하지 않음

추상 클래스 + 상속 : 추상 클래스와 상속을 결합해서 생각해야 함

abstract class SuperClass{
  public void methodA(){
     sysout("methodA~")
  }
  public abstract void methodB()
}
  class SubClass extends SupperClass{
}

자식 클래스인 SubClass는 methodB()메서드를 OVerriding 해야함

methodB() 메서드를 Overriding하지 않으면?

-> SubClass는 추상 클래스로 정의되어야 하고, SubClass 역시 객체를 생성할 수 없음 -> 추상 클래스를 부모 클래스로 지정한 자식 클래스는 부모의 모든 추상 메서드를 Overriding해야 객체를 생성할 수 있음.

추상 클래스의 활용

추상 클래스로 객체를 생성하려면 자식 클래스를 작성하고, 추상 메서드들을 Overriding해야 함

  • 추상 클래스를 사용해야 유지보수의 편의성을 높일 수 있음

최소한의 수정으로 원하는 객체를 사용할 수 있게 되어 유지보수성이 좋아짐

내부 클래스

클래스가 다른 클래스를 포함하는 경우, 내부에 포함된 클래스를 내부 클래스라고 함

  • JDK초기 버전부터 지원되었던 개념
  • 파일 크기의 최소화, 보안, 성능 향상, 이벤트 처리 등을 쉽게 하기 위하여 사용됨.

내부 클래스는 정의되는 위치에 따라서 멤버 클래스와 지역 클래스로 나뉨

  • 멤버 클래스 멤버 변수와 동일한 위치에 선언된 내부 클래스 static예약어가 붙은 static멤버와 instance멤버로 나뉨 동일한 클래스뿐만 아니라 다른 클래스에서도 활용될 수 있음 클래스의 멤버 변수와 성격이 비슷함

  • 지역 클래스 메서드 내에 클래스가 정의되어 있는 경우 지역 클래스(이름을 가지고 있음)와 무명 클래스(이름을 갖지 않음)로 나뉨 활용 범위가 메서드 블록 내부로 제한되는 특징을 갖는 등 지역 변수와 성격이 비슷함.

자바 클래스 구조를 더 조직화, 소스코드를 구현 시 효율을 높일 수 있음

내부 클래스가 생성되기 위해서 외부 클래스의 객체가 반드시 필요함

Instance 멤버 내부 클래스

instance멤버 내부 클래스는 클래스의 멤버와 동일한 위체에서 선언되는 내부 클래스 멤버 변수나 메서드와 동일한 위치에서 선언되었기 때문에 다른 외부의 클래스에서도 사용 가능함.

// 외부 클래스(Top Level 클래스)
class Outside{
  //내부 클래스
  public class Inside{
    // 내부 클래스를 일반 멤버 변수와 동일한 위치에 정의함
  }
}

public class InnerClassTest{
  public static void main(String args[]){
     Outside outer = new Outside();
     Outside.Inside inner = outer.new Inside();
     //내부 클래스의 객체 생성을 위해 외부 클래스의 객체를 생성해야 함
  }
}

--컴파일--> Outside.class -> Outside$inside.class : 외부 클래스명$내부클래스명.class

static 멤버 내부 클래스

내부 클래스를 정의할 때도 static예약어를 사용할 수 있음. 외부 클래스의 객체를 생성하지 않고도 내부 클래스 객체를 생성할 수 있음.

class Outside{
  //Static내부 클래스
  public static class StaticInner{
    //
  }
}

public class FirstApp{
  public static void main(String args[]){
     Outside.StaticInner sinner = new Outside.StaticInner();
     //static 내부 클래스 객체를 생성하는데, 이때 외부 클래스의 객체를 생성하지 않아도 생성
  }
}

이름이 있는 지역 내부 클래스

메서드 내부에서 정의된 클래스로서 지역 변수와 동일한 범위를 가짐 클래스의 이름이 명시되는 클래스임

class Animal{
  void performBehavior(){
     //이름이 있는 지역 내부 클래스
     class Brain{
       // 지역 내부 클래스를 정의함. 지역 변수와 동일한 범위를 가지기 때문에 클래스가 선언된 메서드 블록 내에서만 사용 가능함.
     }
  }
}
public class InnerClassTest{
   public static void main(String args[]){
      Animal ani = new Animal();
      Animal.performBehavior();
   }
}

--컴파일--> Animal.class Animal$1$Brain.class : 외부 클래스명 $1$지역 내부클래스명

이름이 없는 지역 내부 클래스

이름을 갖지 않는 내부 클래스임 new 예약어 뒤에 명시된 클래스가 기존의 클래스인 경우에는 자동적으로 이 클래스의 자식 클래스가 됨 이름이 없기 때문의 무명의 내부 클래스라고도 함 이름이 없는 지역 내부 클래스는 추상 클래스의 객체를 내부 클래스 형태로 생성할 때 자주 사용됨 추상 클래스는 추상 메서드를 포함하고 있기 때문에 객체를 생성해야 함 -> 추상 클래스를 상속한 클래스가 만들어져 객체를 생성해야 함 -> 간단하게 이름이 없는 지역 내부 클래스로 만들어 사용하는 것이 편리할 수 있음

형변환

객체 형변환의 개요

  • 객체 참조변수의 경우에도 형변환(Casting)이 이루어짐

객체 참조변수들 간의 대입 규칠 leftObjRef(부모 클래스) = rightObjRef(자식 클래스)

왼쪽 항과 오른쪽 항의 객체 유형이 서로 다른 경우 두 유형이 서로 상속 관계에 있음 왼쪽 객체가 오른쪽 객체의 상위 클래스인 경우에만 묵시적 형변환이 일어남 자식 클래스에서 부모 클래스 유형으로 할당하는 것은 가능하지만, 반대의 경우 명시적 형변환을 해야함.

부모 클래스 유형을 자식 클래스 유형으로 강제 형변환하는 경우에는? -> 할당되는 인스턴스 유형에 따라서 실행 오류 발생

내부 특성 클래스 형이 다른 클래스 형으로 변환될 수 있는지 여부를 수시로 판단해야 함 instanceof 연산자를 사용함

  • 생성된 객체가 class와 관계있는 형(type)으로 만들어졌는지 확인
  • true 또는 false값을 반환 <생성된 객체 reference 변수> instanceof <class 또는 interface 명>
class Employee2{}

class Manager extends Employee2{}

public class instanceOfTest {
	public static void main(String[] args) {
		Manager m = new Manager();
		Employee2 e = new Employee2();
		System.out.println(m instanceof Manager);
		System.out.println(m instanceof Employee2);
		System.out.println(e instanceof Manager);
	}
}

Manager객체 m은 Manager 객체의 형(type)인 동시에 Employee의 자식 클래스 -> Employee형임 Employee객체 e는 Manager의 부모 클래스로서, Manager 클래스가 가진 새로운 특징은 가지고 있지 않음. ->Manager형은 아님

클래스의 형변환은 기본적으로 상속 관계가 아닌 클래스 사이에는 발생하지 않음 자식 클래스의 객체는 부모 타입의 참조 변수에 할당될 수 있음(묵시적 형변환)

형변환에 참여한 서로 상속 관계에 있는 두 클래스 간에는 동일한 이름의 변수가 존재하거나 메서드가 Overriding 되어 있을 수 있음 생성된 객체 변수를 통해 멤버에 접근할 떄 주의해야 함.

class Parent{
  int num 10;
  void printNum(){
     sysout(num);
  }
}

class Child extends Parent{
  int num 20;
  void printNum(){
     sysout(num);
  }
}

public clas ObjectCastTest{
  public static void main(String args[]){
    Parent p = new Child();
    p.printNum();
    sysout(p.num)
  }
}

-> 20 10

실행결과가 예상과 다르게 출력되는 이유?

20 -> 변수에 대한 접근은 객체의 유형에 의해 결정됨 10 -> 메서드 호출은 할당되는 인스턴스에 의해 결정됨

객체 참조 변수가 변수나 메서드를 참조하는 경우, 참조 관계를 결정하는 시간이 다르기 때문에 나타나는 차이임.