추상 클래스

사전적 의미로 추상(abstract)은 실체 간에 공통되는 특성을 추출한 것을 말한다.

예를 들어 새, 곤충, 물고기 등의 실체에서 공통되는 특성을 추출해보면 동물이라는 공통점이 있다.

여기서 동물은 구체적인 실체라기 보다는 실체들의 공통되는 특성을 가지고 있는 추상적인 것이라고 볼 수 있다.

 

객체를 직접 생성할 수 있는 클래스를 실체 클래스라고 한다면 이 클래스들의 공통적인 특성을 추출해서 

선언한 클래스를 추상 클래스라고 한다. 추상 클래스와 실체 클래스는 상속의 관계를 가지고 있다.

추상 클래스가 부모, 실체 클래스가 자식으로 구현되어 실체 클래스는 충상 클래스의 모든 특성을 물려받고,

추가적인 특성을 가질 수 있다. ( 특성은 필드와 메소드를 말함 )

 

 

추상 클래스의 용도

실체 클래스의 공통적인 특성(필드, 메소드)을 뽑아내어 추상 클래스를 만드는 이유는 2가지가 있다.

 

1. 공통된 필드와 메소드의 이름을 통일할 목적

실체 클래스를 설계하는 사람이 여러 사람일 경우, 실체 클래스마다 필드와 메소드가 제각기 다른 이름을 

가질 수 있다. 예) Phone이라는 추상 클래스에 소유자인 owner 필드와 turnOn() 메소드를 선언하고,

Telephone과 SmartPhone을 상속함으로써 필드와 메소드 이름을 통일할 수 있다.

 

2. 실체 클래스를 작성할 때 시간 절약

공통적인 필드와 메소드는 추상 클래스인 Phone에 모두 선언해두고, 다른 점만 실체 클래스에 선언하면

실체 클래스를 작성하는데 시간을 절약할 수 있다.

 

 

 

일반적으로 개발 프로젝트에서 설계자와 코더(코드를 작성하는 사람)는 다른 일을 수행한다.

설계자는 코더에게 클래스는 어떤 구조로 작성해야 한다는 것을 알려주어야 한다.

이를 단순히 문서로 전달한다면, 코더가 실수로 필드와 메소드 이름을 다르게 코딩할 수 도 있다.

코더가 작성해야 할 클래스가 다수이고 이 클래스들이 동일한 필드와 메소드를 가져야 할 경우, 설계자는

이 내용들을 추려내어 추상 클래스로 설계 규격을 만드는 것이 좋다. 그리고 코더에게 추상 클래스를 상속해서

구체적인 클래스를 만들도록 요청하면 된다.

 

 

 

추상 클래스 선언

추상 클래스를 선언할 때에는 클래스 선언에 abstract 키워드를 붙여야 한다.

abstract를 붙이면 new 연산자를 이용해서 객체를 만들지 못하고, 상속을 통해 자식 클래스만 만들 수 있다.

public abstract class 클래스 {
//필드
//생성자
//메소드
}
실체 클래스 생성

추상 클래스는 실체 클래스의 공통되는 필드와 메소드를 추출해서 만들었기 때문에 
객체를 직접 생성해서 사용할 수 없다.
Animal animal = new Animal(); <= X

추상 클래스는 새로운 실체 클래스를 만들기 위해 부모 클래스로만 사용된다.
코드로 설명하면 추상 클래스는 extends 뒤에만 올 수 있는 클래스이다.
Class Ant extends Animal {...} <= O

 

추상 클래스 실습 

package sec03.exam01;
// 추상클래스 basic
public abstract class Aaa {
	
	public static void main(String[] ar) {
//		Aaa a1 = new Aaa(); 추상클래스는 객체 생성 불가
		
		Baa a2 = new Baa();
		Aaa a3 = a2; // 필드 다형성
		
		
	}
}

class Baa extends Aaa {
	
}

 

 

 

추상 메소드와 재정의

추상 클래스는 실체 클래스가 공통적으로 가져야 할 필드와 메소드들을 정의해놓은 추상적인 클래스로

실체 클래스의 멤버(필드, 메소드)를 통일하는 데 목적이 있다. 모든 실체들이 가지고 있는 메소드의 실행

내용이 동일하다면 추상 클래스에 메소드를 작성하는 것이 좋을 것이다.

하지만 반대로 메소드의 선언만 통일하고, 실행 내용은 실체 클래스마다 달라야 하는 경우도 있는데 예를

들어서 모든 동물은 소리를 내기 때문에 sound(); 라는 메소드를 추상 클래스에 정의를 하고 각각 동물의

소리는 실체 클래스에서 직접 다르게 재정의하면 된다.

추상 메소드는 abstract 키워드와 함께 메소드의 선언부만 있고 메소드 실행 내용인 중괄호{}가 없는 메소드

[public | protected] abstract 리턴타입 메소드이름(매개변수, ...);

추상 클래스 설계 시 하위 클래스가 반드시 실행 내용을 채우도록 강제하고 싶은 메소드가 있을 경우 해당 

메소드를 추상 메소드로 선언한다. 자식 클래스는 반드시 추상 메소드를 재정의해서 실행 내용을 작성해야 하는데,

그렇지 않으면 컴파일 에러가 발생한다.

공통적인 특징을 규정하기 위해 sound() 메소드를 추상 메소드로 선언 방법

public abstract class Animal {
	public abstract void sound();
}

 

 

Animal 클래스를 상속하는 하위 클래스는 동물마다 고유한 소리를 내도록 sound()메소드를 재정의해야 한다

Dog와 Cat 클래스에서 sound() 메소드를 재정의해야 한다.

 

추상 메소드 재정의 실습 1

추상 메소드 선언


package sec03.exam01;
// 추상 메소드 선언 basic
public abstract class Aaa2 {
	void method() {
		
	}
	abstract void method2();// 추상 메소드
}


추상 메소드 재정의
package sec03.exam01;
// 추상 메소드 재정의 basic
public class Bbb extends Aaa2 {

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

}

 

 

추상 메소드 재정의 실습 2

추상 메소드 선언


package sec03.exam02;

// 추상클래스, 추상메소드 선언
public abstract class Animal { // 추상클래스
	public String kind;
	
	public void breathe() {
		System.out.println("숨을 쉽니다.");
	}
	
	public abstract void sound(); // 추상 메소드
}



추상 메소드 재정의 1

package sec03.exam02;
//추상 메소드 재정의
public class Cat extends Animal {
	public Cat() {
		this.kind = "포유류";
	}

	@Override
	public void sound() { 
		System.out.println("야옹");
		
	}
	
	
}

추상 메소드 재정의 2
package sec03.exam02;
//추상 메소드 재정의
public class Dog extends Animal {
	public Dog() {
		this.kind = "포유류";
	}

	@Override
	public void sound() { // 추상 메소드 재정의
		System.out.println("멍멍");
		
	}
	
	
}



실행
package sec03.exam02;
// 실행 
public class AnimalEx {

	public static void main(String[] args) {
		Dog dog = new Dog();
		Cat cat = new Cat();
		dog.sound();
		cat.sound();
		System.out.println("-----");
		
		//변수의 자동 타입 변환
		Animal animal = null;
		animal = new Dog();// 자동 타입 변환 및 재정의된 메소드 호출
		animal.sound();
		animal= new Cat();// 자동 타입 변환 및 재정의된 메소드 호출
		animal.sound();
		System.out.println("-----");
	
		//메소드의 다형성
		animalSound(new Dog());
		animalSound(new Cat());
		
	
	}
	public static void animalSound(Animal animal) {
		animal.sound();// 재정의된 메소드 호출
	}
	
}
<결과>
멍멍
야옹
-----
멍멍
야옹
-----
멍멍
야옹

 

추상 메소드 재정의 실습 3

추상 메소드 선언

package sec03.exam03;

public abstract class Http {// 추상클래스
	public abstract void service();
}


추상 메소드 재정의 1
package sec03.exam03;

public class HttpChild extends Http { 

	@Override
	public void service() {
		System.out.println("로그인 합니다.");
		
	}

	
	
}


추상 메소드 재정의 2
package sec03.exam03;

public class HttpChild2 extends Http {

	@Override
	public void service() {
		System.out.println("파일 다운로드합니다.");
		
	}
	
}


실행
package sec03.exam03;

public class HttpEx {

	public static void main(String[] args) {
		method(new HttpChild());
		method(new HttpChild2());

	}
	public static void method(Http http) {
		http.service();
	}
	

}
<결과>
로그인 합니다.
파일 다운로드합니다.

 

 

 

 

 

인터페이스

자바에서 인터페이스는 객체의 사용 방법을 정의한 타입이다.

인터페이스를 통해 다양한 객체를 동일한 사용 방법으로 이용할 수 있다.

인터페이스에 장점 중 하나는 개발 코드를 수정하지 않고 사용하는 객체를 변경할 수 있도록하는 장점이 있다.

인터페이스는 하나의 객체가 아니라 여러 객체들과 사용이 가능하므로 어떤 객체를 사용하느냐에 따라서 실행

내용과 리턴값이 다를 수 있다. 따라서 개발 코드 측면에서는 코드 변경없이 실행 내용과 리턴값을 다양화할 수

있다는 장점을 가지게 된다.

 

인터페이스 선언

인터페이스 선언은 class 키워드 대신에 interface 키워드를 사용한다.

인터페이스 이름은 클래스 이름을 작성하는 방법과 동일하다.

[public] interface 인터페이스이름 {...}

 

 

클래스는 필드, 생성자, 메소드를 구성 멤버로 가지는데 비해, 인터페이스는 상수 필드와 추상 메소드만을 구성 멤버로

가진다. 인터페이스는 객체로 생성할 수 없기 때문에 생성자를 가질 수 없다.

 

상수 필드 선언

인터페이스는 객체 사용 방법을 정의한 것이므로 실행 시 데이터를 저장할 수 있는 인스턴스 또는 정적 필드를 선언

할 수 없다. 그러나 상수필드는 선언이 가능하다. 단, 상수는 인터페이스에 고정된 값으로 실행 시에 데이터 변경 불가

따라서 인터페이스에 선언된 필드는 모두 public static final의 특성을 갖는다. public static final을 생략하더라도

컴파일 과정에서 자동으로 붙게 된다.

[public static final] 타입 상수이름 = 값;
상수 필드 선언

public interface RemoteControl {
	
	public int MAX_VOLUME = 10;
	public int MIN_VOLUME = 0;
    }

 

 

 

추상 메소드 선언

인터페이스를 통해 호출된 메소드는 최종적으로 객체에서 실행된다. 그렇기 때문에 인터페이스의 메소드는

실행 블록이 필요 없는 추상 메소드로 선언한다. 추상 메소드는 리턴 타입, 메소드이름, 매개 변수만 기술되고

중괄호{ }를 붙이지 않는 메소드를 말한다. 인터페이스에 선언된 추상 메소드는 모두 public abstract의 특성을

갖기 때문에 public abstract를 생략하더라도 컴파일 과정에서 자동으로 붙게 된다.

 

상수필드, 추상 메소드 선언

package sec01.exam01;
// 상수필드, 추상 메소드 선언
public interface RemoteControl {
	// 상수
	public int MAX_VOLUME = 10;
	public int MIN_VOLUME = 0;
	
	// 추상 메소드
	public void trunOn();
	public void trunOff();
	public void setVolume(int volme); // 메소드 선언부만 작성
}

 

 

 

인터페이스 구현

개발 코드가 인터페이스 메소드를 호출하면 인터페이스는 객체의 메소드를 호출한다. 객체는 인터페이스에서

정의된 추상 메소드와 동일한 메소드 이름, 매개 타입, 리턴 타입을 가진 실체 메소드를 가지고 있어야 한다.

이러한 객체를 인터페이스의 구현 객체라고 하고, 구현 객체를 생성하는 클래스를 구현 클래스라고 한다.

 

구현 클래스

구현 클래스는 보통의 클래스와 동일한데, 인터페이스 타입으로 사용할 수 있음을 알려주기 위해 클래스 선언부에

implements 키워드를 추가하고 인터페이스 이름을 명시해야 한다. 그리고 인터페이스에 선언된 추상 메소드의 

실체 메소드를 선언해야 한다.

public class 구현클래스이름 implements 인터페이스이름 {
	//인터페이스에 선언된 추상 메소드의 실체 메소드 선언
}
구현 클래스 1

package sec01.exam01;

public class Television implements RemoteControl {
	// 필드
	private int volume;
	
	
	@Override
	public void trunOn() {
		System.out.println("Tv를 켭니다.");
		
	}

	@Override
	public void trunOff() {
		System.out.println("Tv를 끕니다.");
		
	}

	@Override
	public void setVolume(int volme) {// 인터페이스 상수를 이용 volume필드의 값을 제한
		if(volme > RemoteControl.MAX_VOLUME) {
			this.volume = RemoteControl.MAX_VOLUME;
		}else if(volme < RemoteControl.MIN_VOLUME) {
			this.volume = RemoteControl.MIN_VOLUME;
		}else {
			this.volume = volme;
		}
		System.out.println("현재 TV 볼륨 : " + this.volume);
	}



}


구현 클래스 2

package sec01.exam01;
// 구현 클래스
public class Audio implements RemoteControl{
	// 필드
	private int volume;
	
	@Override
	public void trunOn() {
		System.out.println("Audio를 켭니다.");
		
	}

	@Override
	public void trunOff() {
		System.out.println("Audio를 끕니다.");
		
	}

	@Override
	public void setVolume(int volme) {// 인터페이스 상수를 이용 volume필드의 값을 제한
		if(volme > RemoteControl.MAX_VOLUME) {
			this.volume = RemoteControl.MAX_VOLUME;
		}else if(volme < RemoteControl.MIN_VOLUME) {
			this.volume = RemoteControl.MIN_VOLUME;
		}else {
			this.volume = volme;
		}
		System.out.println("현재 Audio 볼륨 : " + this.volume);
	}

}

 

구현 클래스가 작성되면 new 연산자로 객체를 생성할 수 있다. 하지만 다음 코드는 인터페이스를 사용한 것이 아님

Television tv = new Television();

인터페이스로 구현 객체를 사용하려면 다음과 같이 인터페이스 변수를 선언하고 구현 객체를 대입해야 한다.

인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장한다.

인터페이스 변수에 구현 객체 대입

package sec01.exam01;

public class RemoteConEx {
	
	

	public static void main(String[] args) {
		RemoteControl rc;
		rc = new Television();
		//rc.trunOn();
		//rc.setVolume(5);
//		excute(rc, 7);
		
		
//		rc = new Audio();
//		//rc.setVolume(6);
//		//rc.trunOff();
//		excute(rc, 5);
		
//		Searchable se = new Television();
//		se.search("www.naver.com");
		
//		Searchable se = (Searchable) rc;
//		se.search("www.naver.com");

	}
	
	public static void excute(RemoteControl rc, int vol) { // 해당 코드로 메서드 모두 호출 가능
		rc.trunOn();
		rc.trunOff();
		rc.setVolume(vol);
	}
}

 

 

 

다중 인터페이스 구현 클래스

객체는 다음 그림과 같이 다수의 인터페이스 타입으로 사용할 수 있다.

인터페이스 A와 인터페이스 B가 객체의 메소드를 호출할 수 있으려면 객체는 이 두 인터페이스를 모두 구현해야

한다. 따라서 구현 클래스는 위에 코드 처럼 작성되어야 한다.

다중 인터페이스를 구현할 경우, 구현 클래스는 모든 인터페이스의 추상 메소드에 대해 실체 메소드를 작성해야 한다.

 

다음은 인터넷을 검색할 수 있는 Searchable 인터페이스 이다. search() 추상 메소드는 매개값으로 인터넷 웹사이트

주소 ( URL )를 받습니다.

인터넷 검색할 수있는 search() 메소드

package sec01.exam01;

public interface Searchable {
	void search(String url);
}

만약 SmartTelevision이 인터넷 검색 기능도 제공한다면 다음과 같이 RemoteControl과 Searchable을 모두 구현한

SmartTelevision 클래스를 작성할 수 있다.

다중 인터페이스 구현 클래스

package sec01.exam01;
// 구현 클래스
public class SmartTelevision implements RemoteControl, Searchable {
	// 필드
	private int volume;
	 
	
	@Override
	public void trunOn() {
		System.out.println("Tv를 켭니다.");
		
	}

	@Override
	public void trunOff() {
		System.out.println("Tv를 끕니다.");
		
	}

	@Override
	public void setVolume(int volme) {
		if(volme > RemoteControl.MAX_VOLUME) {
			this.volume = RemoteControl.MAX_VOLUME;
		}else if(volme < RemoteControl.MIN_VOLUME) {
			this.volume = RemoteControl.MIN_VOLUME;
		}else {
			this.volume = volme;
		}
		System.out.println("현재 TV 볼륨 : " + this.volume);
	}
	
    // Searchable search()메소드 재정의
	@Override
	public void search(String url) {
		System.out.println(url + "을 검색합니다.");
		
	}

}

SmartTelevision 클래스는 RemoteControl과 Searchable 인터페이스를 모두 구현하고 있기 때문에 다음 예제와 같이

SmartTelevision 객체를 RemoteControl 타입 변수와 Searchable 타입 변수에 각각 대입할 수 있다.

인터페이스 변수에 구현 객체 대입

package sec01.exam01;

public class SmartTelevisionEx {

	public static void main(String[] args) {
		SmartTelevision tv = new SmartTelevision();
		
		RemoteControl rc = tv;
		Searchable searchable = tv;
		
	}

}

 

 

 

인터페이스 사용

구현 객체가 인터페이스 변수에 대입된다는 것을 알았았으니, 지금부터 인터페이스로 구현 객체를 사용하는 방법을

알아보겠습니다.

클래스를 선언할 때 인터페이스는 필드, 생성자 또는 메소드의 매개변수, 생성자, 또는 메소드의 로컬 변수로 선언될

수 있습니다.

 

인터페이스 사용_선언

package sec01.exam01;

public class MyClass {
	// 필드
	RemoteControl rc = new Television();

	// 생성자
	MyClass() {
	}

	MyClass(RemoteControl rc) {
		this.rc = rc;
		rc.trunOn();
		rc.setVolume(5);
	}

	// 메소드
	void methodA() {
		RemoteControl rc = new Audio();
		rc.trunOn();
		rc.setVolume(5);
	}

	void methodB(RemoteControl rc) {
		rc.trunOn();
		rc.setVolume(5);
	}
}


인터페이스 사용_실행

package sec01.exam01;

public class MyClassEx {

	public static void main(String[] args) {
		System.out.println("1)----------------");
		
		MyClass myClass1 = new MyClass();
		myClass1.rc.trunOn();
		myClass1.rc.setVolume(5);
		
		System.out.println("2)----------------");
		
		MyClass myClass2 = new MyClass(new Audio());
		
		System.out.println("3)----------------");
		
		MyClass myClass3 = new MyClass();
		myClass3.methodA();
		
		System.out.println("4)----------------");
		
		MyClass myClass4 = new MyClass();
		myClass4.methodB(new Television());
	}
}
<결과>
1)----------------
Tv를 켭니다.
현재 TV 볼륨 : 5
2)----------------
Audio를 켭니다.
현재 Audio 볼륨 : 5
3)----------------
Audio를 켭니다.
현재 Audio 볼륨 : 5
4)----------------
Tv를 켭니다.
현재 TV 볼륨 : 5

 

 

실습해보기

인터페이스 연습 1 

인터페이스 정의

package sec01.exam03;

public interface Figure {
	void draw();
}


인터페이스 구현 1

package sec01.exam03;

public class Circle implements Figure {

	@Override
	public void draw() {
		System.out.println("Circle의 draw 메소드");
		
	}
	
}


인터페이스 구현 2
package sec01.exam03;

public class Rectangle implements Figure {

	@Override
	public void draw() {
		System.out.println("Rectangle의 draw 메소드");
		
	}
	
}


인터페이스 구현 3

//인터페이스 구현3

package sec01.exam03;

public class Square implements Figure {

	@Override
	public void draw() {
		System.out.println("Square의 draw 메소드");
		
	}

}


구현한 인터페이스 정의

package sec01.exam03;

public class FigureFactory {
	public Figure getFigure(String figureType) {
		if(figureType == null) {
			return null;
		}
		// equalsIgnoreCase => 대소문자 상관없이 비교 
		if (figureType.equalsIgnoreCase("CIRCLE")) {
			return new Circle();
		}else if (figureType.equalsIgnoreCase("RECTANGLE")) {
			return new Rectangle();
		}else if (figureType.equalsIgnoreCase("SQUARE")) {
			return new Square();
		}
		
		return null;
	}
}


실행

package sec01.exam03;

public class FactoryPatternEx {

	public static void main(String[] args) {
		FigureFactory figureFactory = new FigureFactory();
		
		Figure fig1 = figureFactory.getFigure("CIRCLE");
		
		// Circle의 draw 메소드 호출
		fig1.draw();
		
		Figure fig2 = figureFactory.getFigure("RECTANGLE");
		
		// RECTANGLE의 draw 메소드 호출
		fig2.draw();
		
		Figure fig3 = figureFactory.getFigure("SQUARE");
		
		// SQUARE의 draw 메소드 호출
		fig3.draw();
			
	}

}
<결과>
Circle의 draw 메소드
Rectangle의 draw 메소드
Square의 draw 메소드

 

인터페이스 연습 2

인터페이스 선언

package sec01.exam03_1;

public interface Animal {
	void eat();
}


다중 인터페이스 선언

package sec01.exam03_1;

public interface Bird extends Animal {
	void fly();
}


인터페이스 재정의
package sec01.exam03_1;

public class Sparrow implements Bird {

	@Override
	public void eat() {
		System.out.println("먹는다.");
		
	}

	@Override
	public void fly() {
		System.out.println("날아오른다.");
		
	}

}


실행

package sec01.exam03_1;

public class SparrowEx {

	public static void main(String[] args) {
		Sparrow sparrow = new Sparrow();
		sparrow.eat();
		sparrow.fly();

	}

}
<결과>
먹는다.
날아오른다.

 

인터페이스 연습 3

인터페이스 선언

package sec01.exam03_3;

public interface Calculator {
	void add(int a, int b);
	void subtract(int a, int b);
}


다중 인터페이스 구현 1

package sec01.exam03_3;

public interface Adder extends Calculator {
	void add(int a, int b);
}


다중 인터페이스 구현 2

package sec01.exam03_3;

public interface Subtractor extends Calculator {
	void subtract(int a, int b);
}


인터페이스 재정의 1

package sec01.exam03_3;

public class AdderClass implements Adder {

	@Override
	public void subtract(int a, int b) {
		

	}

	@Override
	public void add(int a, int b) {
		int add = a+b;
		System.out.println("더하기 = " + add);
	}
	


}


인터페이스 재정의 2

package sec01.exam03_3;

public class SubtractorClass implements Subtractor {

	@Override
	public void add(int a, int b) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void subtract(int a, int b) {
		int subtract = a-b;
		System.out.println("빼기 = " + subtract);
		
	}

}


실행

package sec01.exam03_3;

public class Main {

	public static void main(String[] args) {
		AdderClass adderClass = new AdderClass();
		adderClass.add(10, 20);
		
		SubtractorClass subtractorClass = new SubtractorClass();
		subtractorClass.subtract(20, 10);
	}

}
<결과>
더하기 = 30
빼기 = 10

 

'프로젝트 기반 자바(JAVA) 응용 SW개발자 취업과정' 카테고리의 다른 글

2023-06-12 17일차  (1) 2023.06.12
2023-06-09 16일차  (0) 2023.06.09
2023-06-07 14일차  (0) 2023.06.07
2023-06-02 13일차  (0) 2023.06.02
2023-06-01 12일차  (0) 2023.06.01

+ Recent posts