본문 바로가기

IT/개발공부

Java 인터페이스, 다형성 (추상 클래스, 열거 자료형, 익명 클래스)

반응형

 

 

본 게시물은 개인 공부용 포스트 입니다.

 

 

1. 추상 클래스

추상 메소드

  • 메소드 선언 - abstract 키워드 사용
  • 추상 메소드 : 몸체의 구현이 없이 형식만 존재하는 메소드.
    • 반환형, 이름, 인자 선언만 존재
    • 자식 클래스에 상속될 때, 몸체의 구현이 필요
    • 상반된 의미의 final과 함께 사용 x

예)

abstract public class Shape{				// 추상 클래스이기 때문에 객체 생성 안됨
	...
    abstract public double getArea();		// 모양이 정해지지 않았기 때문에 면적을 계산할 수 없음
 }

 

 

 

추상 클래스

  • 클래스 정의에 abstract 키워드 사용
  • 데이터 필드나 일반 메소드 포함할 수 있음
  • 추상 클래스 - 객체 생성을 할 수 없음
  • 추상 메소드를 포함하는 클래스는 반드시 추상 클래스라고 함.
  • 의미적으로 유사한 클래스를 묶고자 할 때 사용 -> 공통으로 사용할 데이터 필드와 메소드를 정의
  • 추상 클래스는 불완전한 클래스이다 -> 기능적으로 구현하기 어려운 메소드가 존재
  • 자식 클래스는 객체 생성이 가능 
  • 자식 클래스가 추상 메소드를 구현하지 않으면 계속해서 자식 클래스도 추상 클래스로 남음
  • 추상 클래스는 일반 클래스와 인터페이스의 중간적 성격을 가짐

 

 

2. 인터페이스

  • 인터페이스 : 100% 추상적 클래스 -> 인터페이스의 모든 메소드가 추상 메소드임
    • 단, 몸체가 구현된 default 메소드와 static 메소드도 포함 가능 -> 모든 메소드의 기본 접근 제어자는 public
    • 데이터 필드는 클래스 상수만 가능 (public static fianl)
  • 참조 자료형이며 직접적 객체 생성은 불가. (객체 생성이 아닌 부모로써의 역할)
  • 인터페이스의 이름은 보통 형용사이다. (예. Runnable, Serializable 등)
  • 인터페이스 정의는 클래스 정의와 유사
    • class 대신 interface를 사용
    • 메소드는 기본적으로 public abstract이다.
    • default 메소드와 static 메소드도 가능 -> 이 경우 몸체를 구현해야 함 (기본적으로 public)
  • 추상 클래스와 마찬가지로 자식 클래스에 상속되어 사용됨
    • 인터페이스를 상속받는 자식 클래스는 모든 추상 메소드를 구현해 주어야 함
  • 의미적으로는 관련 없으나 기능적으로 유사한 클래스들을 묶을 때 인터페이스를 사용할 수 있음
  • 인터페이스를 상속받아 자식 인터페이스를 정의할 수 있음
    • 키워드 extends 사용
    • 여러 인터페이스를 상속받는 다중 상속도 가능함.
  • 자식 클래스가 부모 인터페이스를 상속받는 경우 자식은 부모가 나열한 기능(추상 메소드)을 구현해야 함
    • 구현을 통해 클래스를 정의할 때 implements 사용

예)

class MovablePoint implements Movable{...}		//MovablePoint가 자식 클래스, Movable이 부모 인터페이스

 

 

 

 

예제)

interface Movable{								// 인터페이스
	void moveUp();
    void moveDown();
    void moveLeft();
    void moveRight();
}


public class MovableTest{
	public static void main(String[] args){
    	Movable m1 = new MovablePoint(5, 5);	//Movable 형으로 MoveblePoint 객체 생성
        System.out.println(m1);					//Point at (5, 5)가 출력됨
        m1.moveUp();
        System.out.println(m1);					//Point at (5, 6)가 출력됨
        m1.moveRight();
        System.out.println(m1);					//Point at (6, 6)가 출력됨
    }
}


class Movable implements Movable{
	private int x, y;
    public MovablePoint(int x, int y){			//생성자
    	this.x = x;
        this.y = y;
    }
    public String toString(){
    	return "Point at(" + x + "," + y + ")";
    }
    public void moveup(){ y++; }
    public void moveDown(){ y--; }
    public void moveLeft(){ x--; }
    public void moveRight(){ x++; }
}

 

 

 

 

 

디폴드 메소드 (기본 몸체가 구현되는 추상 메소드)

  • 인터페이스에서 선언하는 메소드에 기본 구현을 넣을 수 있음
    • 자식 클래스에서 상속받을 때, 디폴트 메소드를 그대로 사용하거나 재정의 해줄 수 있음
    • 메소드 선언 시 default를 사용하고 몸체 구현
  • 인터페이스에 나열된 기능을 확장할 때, 기존 코드의 수정을 피하기 위함 (단순히 추상 메소드가 추가된다면, 이전 인터페이스를 구현한 클래스를 수정해야 함)

예)

//기존 인터페이스
Interface DoIt{
	void doSomething();
    int doSomethingElse(String s);
}

//기존 인터페이스에서 추상 메소드를 추가하게 되면 해당 인터페이스를 구현한 클래스 코드들을 바꿔야함.

Interface DoIt{
	void doSomething();
    int doSomethingElse(String s);
    default boolean didItWork(int i, String s){		// default 메소드를 추가하면 해당 인터페이스를 구현한 클래스의 수정을 안해도 됨
    	...
     }
}

 

 

 

추상 클래스, 인터페이스, 클래스 정리

  • 인터페이스와 클래스는 모두 사용자 정의형이다.
  • extends와 implements에 따라 상위/하위 자료형 관계가 설정됨
  • 상위 유형의 변수는 하위 객체의 참조값을 가질 수 있음 (예. SuperClass super = new SubClass(); )
    • SuperClass가 상위, SubClass가 하위
    • 상위 유형의 변수가 가리키는 객체의 실제 유형에 따라 수행되는 메소드가 결정됨 (동적 바인딩)

 

 

 

 

 

3. 다형성

  • 다형성 : 유사하지만 다양한 형상이나 다양한 기능을 가진다는 뜻
  • 예) 
    • 한 부모에서 나온 두 자식 객체는 비슷하지만 다름
    • 하나의 클래스에서 오버로딩된 메소드들은 유사하지만 조금씩 다른 기능을 수행
    • 자식 클래스에서 재정의 된 메소드는 부모의 것과 유사하지만 다른 기능을 수행

 

 

형 변환

  • 상속 관계에 있는 클래스 간에는 타입 변환이 가능 (전혀 다른 두 클래스 간에는 타입 변환 X)
  • 하위 클래스에서 상위 클래스로의 형 변환은 문제없음 (업캐스팅)
    • 자동으로 형 변환 가능
    • 참조형 변수는 같은 유형의 객체 또는 하위 객체를 참조할 수 있음
    • 예) Animal animal = new Dog();   -> 하위 객체 참조한 것임

 

 

오버라이딩 (재정의)

  • 클래스의 다형성
    • 부모 클래스로부터 상속받은 메소드를 자식 클래스에서 오버라이딩 가능
    • 부모와 자식에서 같은 이름의 메소드가 다른 기능을 수행 (같은 이름과 매개변수 및 반환형을 가지나 몸체가 다름)
  • 인터페이스의 다형성
    • 자식 클래스들에서 상위 인터페이스의 메소드를 다르게 구현

 

 

 

예제)

class A{
	public void func(){
    	System.out.println("a");
    }
}

class B extends A{
	public void func(){
    	System.out.println("b");
    }
}

class C extends B{
	public void func(){
    	System.out.println("c");
    }
}


public class PolymorphTest{
	public static void main(String args[]){
    	A a = new B();
        a.func();		// b가 출력됨
        a = new c();
        a.func();		// c가 출력됨
    }
}

 

 

 

예제 2

class Employee{
	int nSalary;
    String szDept = null;
    public void dojob(){						// 이 메소드가 없으면 에러가 생김 (선언 유형이 Employee이기 때문)
    	System.out.println("Do something");
    }
}

class Sales extends Employee{
	public Sales(){szDept = "Sales Dept";}
    public void dojob(){
    	System.out.println("Do sales");
    }
}

class Development extends Employee{
	public Development(){szDept = "Sales Dept";}
    public void dojob(){
    	System.out.println("Do development");
    }
}


public class Company1{
	public static void main(String args[]){
    	Employee emp1, emp2;
        emp1 = new Sales();
        emp2 = new Development();
        emp1.dojob();			//Do sales가 출력됨
        emp2.dojob();			//Do development가 출력됨
    }
}

 

 

 

 

 

4. 열거 자료형

열거형 정의

  • 열거형 : 미리 정의된 상수값을 만들기 위한 자료형
  • enum을 사용하여 정의함
  • 열거형으로 선언된 변수에는 미리 지정된 값만 대입 가능
  • 상수값을 배열로 return하는 static 메소드로 values()를 제공함

예)

Enum Day{
	SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}


//main 함수에서 사용
Day day = Day.MONDAY;
for(Day d : Day.values()){
	System.out.println(d);			//DAY의 값들이 차례대로 출력됨
}

 

 

 

생성자와 메소드

  • 상수 선언이 필드나 메소드보다 먼저 정의되어야 하며 세미콜론(;)으로 끝나야 한다.
  • 열거형 정의에 필드와 메소드를 포함할 수 있음
  • 생성자는 열거형과 같은 이름을 가지며 접근 제어자는 생략 또는 private이어야 함
  • 열거형에서 상수값은 하나의 객체와 같음
  • 열거형의 생성자는 상수값을 설정(객체 생성)할 때 자동 호출됨

예)

enum BaseballTeam{												//열거형
	LG(40, 30), SS(30, 40), KT(20, 50), SK(35, 35), NC(55, 15);	//상수값 5개
    
    private final int win;
    private final int lose;										//데이터 필드 2개
    
    private BaseballTeam(int win, int lose){					//생성자
    	this.win = win;
        this.lose = lose;
    }
    
    public double winRate(){									//메소드
    	return (win * 100.0) / (win + lose);
    }
}


public class EnumTest2{
	public static void main(String args[]){
    	BaseballTeam bt = BaseballTeam.LG;
        system.out.println(bt.winRate());
    }
}

 

 

5. 익명 클래스

  • 익명 클래스 : 일회성으로 1개의 객체를 생성하기 위한 클래스
    • 클래스 정의와 동시에 객체를 생성할 수 있음
  • 슈퍼 클래스를 상속받거나 인터페이스를 구현하도록 익명 클래스를 정의함
    • new 슈퍼클래스(){ ... }  -> 슈퍼클래스의 자식 객체 생성
    • new 인터페이스(){ ... } -> 인터페이스를 구현하는 자식 객체 생성

 

예)

public class AnonymousTest{
	public static void main(String args[]){
    	CSuper sub = new CSuper(){
        	public int b = 20;
            public void method1(){
            	System.out.println("sub1");
            }
            public void method3(){
            	System.out.pringln("sub3");
            }
        );
        
        sub.method1();				//sub1이 출력됨 (sub의 객체 메소드가 호출됨)
        sub.method2();				//super2가 출력됨 (sub의 부모클래스의 method2 메소드가 호출됨)
        System.out.println(sub.a);	//10이 출력됨 (sub의 부모클래스의 a 변수가 출력됨)
        ...
    }
}


class CSuper{
	public int a = 10;
    public void method1(){
    	System.out.println("super1");
    }
    public void method2(){
    	System.out.println("super2");
    }
}

 

 

예2)

//인터페이스를 구현한 익명 클래스
public class AnonymousTest{
	public static void main(String args[]){
    	MyInterface sub = new MyInterface(){
        	public void method(){
            	System.out.println("sub1");
            }
        };
        
        sub.method();			//sub1이 출력됨
    }
}

interface MyInterface{
	public void method();
}

 

반응형