본문 바로가기

Java/Java 기초

8. 추상 클래스와 인터페이스, 내부 클래스

추상 메소드(함수)와 추상 클래스

추상의 개념은 이력서와 같다고 할 수 있다. 이력서를 작성할 때, 내가 아무리 스펙이 다양하더라도 이력서에서 정해준 내용만 기입해야 한다.

추상 메소드는 이력서처럼 틀을 정해두고, 구체적인 내용을 비워놓아 상속받기 위한 클래스에서는 비어있는 내용을 구체적으로 작성하여 구현해서 사용해야 한다.

추상메소드는 제어자 'abstract'를 붙여서 선언하고, 구체적인 내용이 없으면 내용인 중괄호({ })를 생략하고 세미콜론을 붙인다. 형태는 다음과 같다.

 

abstract void methodA( );

 

추상클래스는 추상메소드를 멤버로 가지는 클래스로 메소드가 추상이면 클래스도 추상으로 선언해줘야 한다. 형태는 다음과 같다.

 

abstract class Cellphone

{

    abstract void methodA( );

}

 

다음은 추상클래스의 예제이다.

abstract class Pokemon
{
	String name;
	
	abstract void attack();
	abstract void sound();
	
	public String getName()
	{
		return this.name;
	}
}

class Pikachu extends Pokemon
{

	Pikachu()
	{
		this.name = "피카츄"; 
	}
	
	@Override
	void attack() {
		System.out.println("전기공격");
	}

	@Override
	void sound() {
		System.out.println("피카피카");
	}

	
}

class Squirtle extends Pokemon
{

	Squirtle()
	{
		this.name = "꼬부기"; 
	}
	
	@Override
	void attack() {
		System.out.println("물공격");
	}

	@Override
	void sound() {
		System.out.println("꼬북꼬부칩");
	}

	
}

public class AbstractClass1 {

	public static void main(String[] args) {
		Pikachu pikachu1 = new Pikachu();
		System.out.printf("이 포켓몬은 %s 입니다\n",pikachu1.getName());
		pikachu1.attack();
		pikachu1.sound();
		
		Squirtle squirtle1 = new Squirtle();
		System.out.printf("이 포켓몬은 %s 입니다\n",squirtle1.getName());
		squirtle1.attack();
		squirtle1.sound();
	}

}

실행결과는 다음과 같다.

 

이 포켓몬은 피카츄 입니다
전기공격
피카피카
이 포켓몬은 꼬부기 입니다
물공격
꼬북꼬부칩

 

pokemon을 추상클래스로 정하고 추상메소드로 공격과 울음소리를 abstract를 통해 상속받는 포켓몬들은 해당 함수들을 필수적으로 구현하도록 한다.

 

인터페이스

인터페이스는 원하는 시점에서 기능을 불러와서 사용하는 부품의 개념이다. 

예를 들면, 핸드폰은 기본적으로 통화, 카메라, 문자, 위치정보 등의 기능을 가지는데 카카오톡에서 페이스톡을 구현하기 위해 핸드폰의 통화기능과 카메라 기능을 불러와서 사용하는 것이 인터페이스를 활용하는 것이다.

상속과의 차이점이라면, 상속은 1개의 부모클래스에 여러개의 자식이 존재하는 구조라면, 인터페이스는 하나의 자식에 여러명에 부모가 존재할 수 있는 구조이다.

인터페이스는 implements를 사용하여 인터페이스를 구현한다. implements 받는 클래스에서 함수를 사용하기 위해선 필수적으로 오버라이딩이 필요하다.

인터페이스는 기능을 정해두고 불러와서 사용만 하는것으로 멤버의 수정이 거의 발생하지 않는다. 형태는 다음과 같다.

 

interface 인터페이스 이름

{

    public static final 데이터타입 변수명 = 값;//변수값 설정(실질적 상수)

    public abstract 반환타입 함수이름(파라미터);//함수 설정

}

 

제어자(public등)를 생략해도 에러가 생기지 않는데 이것은 컴파일러에서 자동으로 제어자를 생성해주기 때문이다.

인터페이스를 오버라이딩 하기위해 부모의 접근제어자보다 더 넓거나 같은범위를 사용해야 하므로 주로 public을 붙이고 protect와 private 사용을 지양해야된다. 

또한 인터페이스들 끼리도 서로 상속이 가능하다. 

인터페이스로 작업하는 방법은 작업자끼리 서로 구현할 기능을 미리 협의하고 반환타입을 정해놓고 작업한다. 이렇게 틀을 정하면 서로의 작업과 무관하게

프로젝트를 진행할 수 있다. 다음 예제는 레저관광 예제이다.

package Chap_8;

import java.util.Scanner;

interface Providable
{
	abstract void leisureSport();
	abstract void sightseeing();
	abstract void food();
}

class KoreaTour implements Providable
{

	@Override
	public void leisureSport() {
		System.out.println("한강에서 수상스키 투어");
	}

	@Override
	public void sightseeing() {
		System.out.println("경복궁 관람 투어");
		
	}

	@Override
	public void food() {
		System.out.println("전주 비빔밥 투어");
		
	}
	
}

class JapenTour implements Providable
{

	@Override
	public void leisureSport() {
		System.out.println("도쿄타워 번지점프 투어");
	}

	@Override
	public void sightseeing() {
		System.out.println("오사카 관람 투어");
		
	}

	@Override
	public void food() {
		System.out.println("초밥 맛집 투어");
		
	}
	
}

class USATour implements Providable
{

	@Override
	public void leisureSport() {
		System.out.println("하와이 제트스키 투어");
	}

	@Override
	public void sightseeing() {
		System.out.println("자유의 여신상 관광 투어");
		
	}

	@Override
	public void food() {
		System.out.println("전통 아메리칸 버거 투어");
		
	}
	
}

class TourGide
{
	int a = 0;
	Providable tour = null;
	
	public TourGide(int choice) 
	{
		switch(choice)								//switch
		{
			case 1:
				tour = new KoreaTour();
				break;
			case 2:
				tour = new JapenTour();
				break;
			case 3:
				tour = new USATour();
				break;
		}
		
//		if(choice==1)								//if 문
//		{
//			tour = new KoreaTour();
//		}
//		else if (choice==2)
//		{
//			tour = new JapenTour();
//		}
//		else
//		{
//			tour = new USATour();
//		}
		
	}
	
	
	
	public void leisureSport()
	{
		tour.leisureSport();
	}
	public void sightseeing()
	{
		tour.sightseeing();
	}
	public void food()
	{
		tour.food();
	}

}

public class Interface3 {

	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		
		System.out.println("[여행 패키지 선택] ");
		System.out.printf("1.한국여행 2.일본여행 3.미국여행 : ");
		int choice = sc.nextInt();
		TourGide guide = new TourGide(choice);
		
		guide.leisureSport();
		guide.sightseeing();
		guide.food();

	}

}

 

실행결과는 다음과 같다

 

[여행 패키지 선택] 
1.한국여행 2.일본여행 3.미국여행 : 3
하와이 제트스키 투어
자유의 여신상 관광 투어
전통 아메리칸 버거 투어

 

Providable를 implements받은 투어클래스들은 모두 오버라이드를 통해 3가지 함수를 구현하였다.

 

인터페이스와 다형성

특정 인터페이스를 구현한 인스턴스는 해당 인터페이스 타입의 참조변수로 참조가 가능하다. 형태는 다음과 같다.

 

인터페이스명 참조변수명 = new 클래스명( );

 

다음은 인터페이스와 다형성 활용의 예제이다.

package Chap_8;

interface Camera
{
	void photo();
}

interface Call
{
	void calling();
}

interface Memo
{
	void write();
}

interface Clock
{
	void clock();
}

class MyCellPhone implements Camera,Call,Memo,Clock
{

	@Override
	public void clock() {
		
	}

	@Override
	public void write() {
		
	}

	@Override
	public void calling() {
		
	}

	@Override
	public void photo() {
		
	}
	
}

class PhonUser
{
	void photo(Camera c)
	{
		System.out.println("사진을 찍었습니다.");
	}
	
	void call(Call c)
	{
		System.out.println("전화를 걸었습니다.");
	}
	
	void clock(Clock c)
	{
		System.out.println("시계를 보여줍니다.");
	}
}

public class Interface4 {

	public static void main(String[] args) {
		MyCellPhone phone1 = new MyCellPhone();
		Camera phone2 = new MyCellPhone();
		Call phone3 = new MyCellPhone();
		Memo phone4 = new MyCellPhone();
		Clock phone5 = new MyCellPhone();
		PhonUser user1 = new PhonUser();
		
		user1.call(phone3);
		user1.call(phone1); 
		user1.clock(phone5); 
		user1.photo(phone2); 
	}

}

실행결과는 다음과 같다.

 

전화를 걸었습니다.
전화를 걸었습니다.
시계를 보여줍니다.
사진을 찍었습니다.

 

내부 클래스

내부 클래스는 중첩 클래스로도 불리며 외부 클래스의 모든 멤버(변수+함수)들을 마치 상속받아 사용하듯이 자신의 멤버인 것처럼 사용할 수 있다.

장점은 따로 멤버설정을 할 필요가 없다는 것이고, 단점은 외부접근이 어렵다. 변수같이 클래스에도 인스턴스 클래스와 스테틱 클래스가 존재한다.

인스턴스 클래스는 클래스 내부에서 인스턴스 멤버처럼 다루어진다.

스테틱 클래스는 스테틱 멤버처럼 다루어지며, 스테틱 멤버들과 상호작용 한다.

 

익명 클래스

이름이 존재하지 않는 클래스로 1번만 사용할 클래스를 임시적으로 생성하는 것이다. 

package Chap_8;

class OuterClass1
{
	void a()
	{
		System.out.println("method a");
	}
	void b() {}
}

public class Annoymous2 {

	public static void main(String[] args) {
		OuterClass1 o = new OuterClass1()
		{
			void a()
			{
				System.out.println("새롭게 정의한 익명클래스 메소드");
			}
		};
		
		o.a();
		OuterClass1 ok = new OuterClass1();
		ok.a();

	}

}

OuterClass1을  main에서 익명클래스로 생성하였다.

'Java > Java 기초' 카테고리의 다른 글

7. 상속과 다형성  (0) 2022.03.24
6. 클래스  (0) 2022.03.14
5. 배열  (0) 2022.03.08
4. 제어문  (0) 2022.03.06
3. 연산자  (0) 2022.03.06