지난 안드로이드 소켓 프로그래밍에서 쓰레드를 이용해 서버에 접속하려고 할 때,
쓰레드 클래스를 상속하여 새로운 MyThread라는 클래스를 생성하였다.
2021/01/31 - [프로그래밍/Android Java] - 안드로이드 소켓 프로그래밍 / 클라이언트 - 서버 접속 #2
자바에서는 클래스 정의에서 키워드를 이용하여 클래스를 상속받거나 인터페이스를 사용할 수 있다.
클래스는 변수(필드)와 메소드의 집합이라는 것은 알고 있다.
그렇다면 인터페이스는 무엇일까?
인터페이스란
인터페이스는 추상 메소드들의 집합이라고 볼 수 있다.
다른 클래스에서 인터페이스를 사용하고자 한다면 인터페이스를 불러와서 그 안의 메소드를 정의해야한다.
인터페이스는 클래스와는 다르게 변수를 가질 수 없다.
인터페이스는 오직 상수와 추상 메소드로만 구성될 수 있다.
인터페이스는 아래와 같이 작성하면 된다.
interface interface_name {
public static final Type constant_name = value;
public abstract Type method_name(parameters);
}
static은 클래스 변수선언을 위한 키워드이고, final은 상수선언을 위한 키워드이다.
abstract는 뜻 그대로 추상 메소드를 선언하기 위한 키워드이다.
키워드에 관해 자세한건 나중에 필요하면 다루기로!
추상클래스란
추상클래스는 클래스이므로 분명 변수와 메소드의 집합이다.
하지만 일반 클래스와는 다르게 내용이 정의되지 않은 메소드를 포함한다.
추상 클래스는 아래와 같이 작성할 수 있다.
public abstract class class_name {
private Type variable_name = value;
public Type method_name_1(parameters) {
// To do
}
public abstract Type method_name_2(parameters);
}
그렇다면 추상 메소드의 집합인 인터페이스와 다를 것이 있을까?
겉으로 보기에는 인터페이스와 달리 클래스 내에 변수를 사용할 수 있다는 것과 추상 메소드가 아닌 메소드가 있을 수 있다는 것이 다르다.
그러나 추상클래스와 인터페이스의 본질은 상속과 다형성에 있다.
추상클래스는 변수와 메소드가 크게 관련이 있는 클래스 간에 코드를 공유하여 활용성을 높이기 위해 사용한다.
예를 들어 Car class와 Motorcycle class가 있는데, 이 둘은 모두 탈 것이라는 공통점이 있다.
따라서 변수는 최대속도, 배기량 등이 있을 수 있고, 메소드에는 '시동을 걸다.', '회전하다.' 등이 있을 수 있다.
이렇게 공통점이 많을 경우에는 Vehicle이라는 추상클래스를 정의하고, Car와 Motorcycle은 Vehicle을 상속하면 코드의 활용성을 높일 수 있고, 유지보수하기에도 용이하다.
public abstract class Vehicle {
private double max_velocity;
private int capacity;
public Vehicle(double max_vel, int cc) {
this.max_velocity = max_vel;
this.capacity = cc;
}
public void rotate(double angle) {
// To do
}
public void print() {
System.out.println("max velocity : " + this.max_velocity);
System.out.println("engine capacity : " + this.capacity);
}
public abstract void start();
}
Vehicle 클래스를 위와 같이 작성했다. 필드에는 최대 속도와 배기량이 들어가고 오브젝트 생성 시 최대속도와 배기량을 입력하여 인스턴스 변수로 설정하도록 한다.
메소드에는 rotate, print, start가 있다.
탈것마다 시동 거는 방식은 조금씩 다를 수 있기 때문에 start는 추상메소드로 작성하여 Vehicle 클래스를 상속받는 Car와 Motorcycle 클래스에서 정의하도록 한다.
다음은 Car와 Motorcycle 클래스를 작성한 것이다.
public class Car extends Vehicle {
public Car(double max_vel, int cc) {
super(max_vel, cc);
}
@Override
public void start() {
// TODO Auto-generated method stub
System.out.print("Press brake pedal > ");
System.out.print("insert key > ");
System.out.print("turn the key > ");
System.out.println("Engine started");
}
}
public class Motorcycle extends Vehicle {
public Motorcycle(double max_vel, int cc) {
super(max_vel, cc);
}
@Override
public void start() {
// TODO Auto-generated method stub
System.out.print("insert key > ");
System.out.print("turn the key > ");
System.out.println("Engine started");
}
}
생성자만 클래스 이름대로 정의하고 추상 메소드 start를 각 클래스에서 재정의했다.
이제 두 클래스로부터 오브젝트를 생성하여 메소드를 실행해보면 다음과 같은 결과가 출력된다.
public class Main {
public static void main(String[] agrs) {
Car mycar = new Car(200, 500);
mycar.print();
mycar.start();
Motorcycle mymc = new Motorcycle(250, 250);
mymc.print();
mymc.start();
}
}
이처럼 추상 클래스를 활용하면 코드의 활용성을 높일 수 있다!
지금까지 작성한 Car 클래스와 Motorcycle 클래스에는 달리는 기능이 없다.
만약 People이라는 사람을 나타내는 추상클래스가 존재하여 사람이 탈것에 탑승한 경우를 생각해보자.
그렇다면 사람이 탑승한 탈것의 클래스를 새로 생성하기 위해 Vehicle 클래스와 People 클래스를 상속받아 새로운 클래스를 작성해야 할까?
그렇지 않다. 자바에서는 다중 상속을 지원하지 않기 때문에 두 개의 클래스를 상속받을 수 없다.
만약 두 클래스를 상속하였다고 생각하고 클래스를 작성해보자
public class ManInCar extends People, Vehicle {
public ManInCar() {
super();
}
public void run() {
}
}
People 클래스와 Vehicle 클래스를 상속받아 ManInCar라는 새로운 클래스를 생성했다.
그렇다면 먼저 생성자에서 인스턴스 변수들을 초기화해야 하는데, super를 call하는 순간 People의 생성자를 사용해야 할지, Vehicle의 생성자를 사용해야 할지 모른다.
또한 People과 Vehicle 모두 run이라는 추상 메소드가 있다고 가정해보자.
이 때, ManInCar 클래스를 정의하려면 추상 메소드 run을 정의해야 한다.
그러나 run의 주체가 People인지 Vehicle인지 결정할 수 없다.
이렇기에 자바에서는 다중상속을 사용할 수 없는 것이다!
People과 Vehicle은 서로 성격이 매우 다른 클래스이다. 그러나 둘 다 달리는 기능은 있을 수 있기에 run 메소드를 정의하고싶다.
달리는 기능은 동물, 사람, 탈것, 기계, 프로그램 등 여러가지에서 쓰일 수 있는 메소드이다.
따라서 사용성이 광범위한 메소드이기때문에 인터페이스로 선언하여 클래스 정의 시에 인터페이스를 불러와서 구현하는 것이 더 효율적일 것이다.
public interface Run {
public abstract void run(double vel);
}
이렇게 Run이라는 인터페이스에 추상 메소드 run을 선언했다.
그렇다면 ManInCar 클래스는 다음과 같이 정의하여 사용할 수 있다.
public class ManInCar extends Vehicle implements Run {
public ManInCar(double max_vel, int cc) {
super(max_vel, cc);
}
@Override
public void run(double vel) {
// TODO Auto-generated method stub
System.out.println("Current velocity : " + vel);
}
@Override
public void start() {
// TODO Auto-generated method stub
System.out.print("Press brake pedal > ");
System.out.print("insert key > ");
System.out.print("turn the key > ");
System.out.println("Engine started");
}
}
그리고 ManInCar 오브젝트를 생성하여 메소드를 다음과 같이 실행하면 된다.
public class Main {
public static void main(String[] agrs) {
ManInCar mic = new ManInCar(200, 500);
mic.print();
mic.start();
mic.run(50);
}
}
실행 결과는 다음과 같다!
클래스는 다중 상속이 되지 않지만 인터페이스는 여러 개의 인터페이스를 불러와 구현할 수 있다.
따라서 활용도가 높은 메소드는 인터페이스로 구현하여 다양한 클래스에서 이용하는 것이 바람직하다고 볼 수 있다.
이렇게 상속과 다형성을 고려하여 추상클래스와 인터페이스를 적절히 활용한다면 훌륭한 객체지향 프로그래밍을 할 수 있지 않을까!!
'프로그래밍 > Java' 카테고리의 다른 글
자바 프로그래밍 / Thread와 Runnuble (0) | 2021.02.13 |
---|---|
자바 프로그래밍 / extends와 implements 차이 (0) | 2021.02.10 |
자바 프로그래밍 / Package란? (0) | 2021.02.05 |