티스토리 뷰

Programming/Java

자바 프로그래밍 - 8

Mongi0417 2023. 6. 20. 20:09

8.1 추상 클래스

추상 클래스 정의

추상 메소드는 몸체를 갖지 않으므로 실행할 수 없는 메소드이다. 추상 메소드가 하나라도 포함되면 그 클래스는 추상 클래스(abstract class)로 정의되어야 한다. 클래스를 정의할 때 class 앞에 abstract를 추가하면 추상 클래스가 된다.

public abstract class MyShape {
	protected int x, y;
    public MyShape(int x, int y) {
    	this.x = x; this.y = y;
    }
    public abstract void draw(Graphics g);
}

하나 혹은 그 이상의 추상 메소드를 가지는 클래스는 추상 클래스가 되어야 하는데, 추상 클래스는 객체를 생성하지 못한다.

MyShape p;
p = new MyShape(10, 10); // 에러

추상 클래스는 일반적으로 추상적 개념에 대응하는데, 그 역할은 다른 클래스를 위한 기반 클래스로 사용하기 위함이다.

 

추상 클래스를 도입하는 이유

추상 클래스의 자손으로 추가되는 모든 클래스는 추상 메소드를 오버라이드하도록 강제된다. 따라서, 추상 클래스 타입의 참조 변수를 사용하는 클라이언트 코드는 메소드 호출이 항상 올바르게 동작하리라고 확신할 수 있게 된다.

 

8.2 인터페이스

인터페이스의 정의

참고) 인터페이스의 구성 요소

원래의 자바 문법에 따르면 인터페이스는 추상 메소드와 이름 상수의 정의로만 구성되었다. 그런데 자바 8부터는 추상 메소드뿐만 아니라 몸체를 가지는 '디폴트' 메소드와 정적 메소드가 인터페이스 내에 허용되도록 수정되었다.

 

- default 접근 지정자

일반적으로 인터페이스 내에 새로운 메소드가 추가된다면, 추가된 메소드 수만큼 구현 클래스들은 강제적으로 구현해야 한다.

하지만 default 메소드는 이러한 문제로부터 벗어날 수 있다. 즉, 인터페이스에 새로운 메소드가 추가된다 할지라도 구현 클래스에서 따로 구현할 필요 없이 바로 사용할 수 있다.

 

타입으로서의 엔터페이스

자바의 자료형은 기본형과 참조형으로 나뉘며 참조형에는 클래스, 배열, 인터페이스 총 3가지가 있다. 즉, 인터페이스는 참조형 타입에 속하며 인터페이스로 객체를 생성할 수는 없지만 참조 변수를 선언할 수 있다는 의미이다.

public class ShapeTest {
    public static void main(String[] args) {
        Measurable m = new MyRect(20, 15);
        System.out.printf("area = %.2f, perimeter = %.2f\n", m.getArea(), m.getPerimeter());
        m = new MyCircle(22);
        System.out.printf("area = %.2f, perimeter = %.2f", m.getArea(), m.getPerimeter());
    }
}

MyRect와 MyCircle은 모두 Measuarable 인터페이스를 구현한다. 따라서 Measurable 타입의 참조 변수가 가리킬 수 있는 것이다. 물론 인터페이스 타입의 참조 변수를 통해서는 그 인터페이스에 선언된 메소드만 호출할 수 있다.

 

인터페이스는 매개변수의 타입으로도 사용될 수 있다. 아래 코드의 정적 메소드 print는 Measurable 타입의 매개변수를 가지고 있으므로 Measurable 인터페이스를 구현하는 어떤 클래스의 객체라도 인자로 넘길 수 있다.

public class ShapeTest {
    public static void main(String[] args) {
        MyRect rect = new MyRect(20, 15);
        print(rect);
        MyCircle circle = new MyCircle(22);
        print(circle);
    }

    public static void print(Measurable shape) {
        System.out.printf("area = %.2f, perimeter = %.2f\n", shape.getArea(), shape.getPerimeter());
    }
}

이러한 경우에도 메소드 print 내에서 매개변수 shape을 통해서는 Measurable 인터페이스의 메소드만 호출할 수 있다.

Measurable 타입의 변수를 통해 메소드가 호출될 때 실행되는 메소드는 동적 바인딩에 의해 결정된다. 이와 같이 인터페이스에서도 동적 바인딩에 의한 다형성이 그대로 적용된다.

 

인터페이스의 상속

인터페이스 간에도 상속이 가능하다. 클래스의 상속과 마찬가지로 extends 키워드를 사용하여 상속할 수퍼 인터페이스를 명시하면 된다.

또한 클래스의 상속과 달리 인터페이스는 다중 상속이 허용되므로 두 개 이상의 인터페이스로부터 상속이 가능하다.

interface InterfaceA {
	void foo();
}

interface InterfaceB {
	void goo();
}

interface InterfaceC extends InterfaceA, interfaceB {
	void zoo();
}

InterfaceC는 InterfaceA와 InterfaceB로부터 상속받으므로 InterfaceC를 구현하는 클래스는 foo, goo, zoo 세 개의 메소드를 모두 구현해야 객체를 생성할 수 있는 일반 클래스가 된다.

 

Comparable 인터페이스

정수의 배열을 크기에 따라 정렬할 때는 Arrays 클래스의 정적 메소드인 sort를 사용하면 간단하게 해결할 수 있다.

public class ArraySortDemo {
    public static void main(String[] args) {
        int[] array = {87, 24, 56, 91, 36, 11};

        Arrays.sort(array);
        for (int n : array)
            System.out.print(n + " ");
    }
}

 

Arrays.sort 메소드는 배열뿐만 아니라 객체의 배열에도 동작하므로 아래의 sort 메소드 호출은 컴파일 에러는 발생시키지 않지만, 실행 에러가 발생한다.

public class StudentSortDemo {
    public static void main(String[] args) {
        Student[] students = new Student[4];
        students[0] = new Student("박철수", 2.23);
        students[1] = new Student("이철수", 1.23);
        students[2] = new Student("김철수", 4.23);
        students[3] = new Student("표철수", 3.23);

        Arrays.sort(students);
        for (Student s : students)
            s.writeInfo();
    }
}

배열을 정렬하는 Arrays.sort() 메소드는 여러 가지 타입의 배열에 대해 오버로드 되어 있다. 여러 타입의 배열에 대한 sort 메소드 버전이 따로 존재한다는 의미이다. 이 메소드가 객체의 배열에 적용될 때는 배열의 요소가 되는 객체가 Comparable 인터페이스를 구현해야 한다는 제약 조건이 있다.

class Student implements Comparable {
    private String name;
    private double gpa;

    public Student(String name, double gpa) {
        this.name = name;
        this.gpa = gpa;
    }

    public void writeInfo() {
        System.out.println(name + " : " + gpa);
    }

    @Override
    public int compareTo(Object o) {
        if (o != null && o instanceof Student) {
            Student s = (Student) o;
            return this.name.compareTo(s.name);
        }
        else
            return -1;
    }
}

Comparable 인터페이스에 명시된 대로 매개변수가 Object 타입으로 선언되어 있다. 이 메소드는 두 개의 Student 객체 간에 적용되어야 하므로 타입 검사가 필요하다. 그리고 매개변수 o는 Object 타입이므로 매개변수 o가 가리키는 객체를 Student 타입의 변수에 대입하는 다운캐스팅이 필요하다.

 

연습문제 01)

public class Programming01 {
    public static void main(String[] args) {
        Drawable r = new MyRect(5, 10, 12, 20);
        r.draw();
        r = new MyCircle(5, 2, 20);
        r.draw();
    }
}

abstract class MyShape implements Drawable {
    protected int x, y;
    
    public MyShape(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

class MyRect extends MyShape {
    private int width;
    private int height;

    public MyRect(int x, int y, int width, int height) {
        super(x, y);
        this.width = width;
        this.height = height;    
    }

    @Override
    public void draw() {
        System.out.printf("x = %d, y = %d, width = %d, height = %d\n", x, y, width, height);
    }
}

class MyCircle extends MyShape {
    private int radius;

    public MyCircle(int x, int y, int radius) {
        super(x, y);
        this.radius = radius;
    }

    @Override
    public void draw() {
        System.out.printf("x = %d, y = %d, radius = %d\n", x, y, radius);        
    }
}
public interface Drawable {
    public void draw();
}

 

연습문제 02)

public class Programming02 {
    public static void main(String[] args) {
        Book[] bookArray = new Book[5];
        bookArray[0] = new Book("정의란 무엇인가", 20000);
        bookArray[1] = new Book("자바 프로그래밍", 38000);
        bookArray[2] = new Book("벌레 이야기", 12000);
        bookArray[3] = new Book("당신들의 천국", 15000);
        bookArray[4] = new Book("마시멜로 이야기", 26000);

        Arrays.sort(bookArray);
        for (Book b : bookArray)
            System.out.println(b.toString());
    }
}


class Book implements Comparable {
    private String title;
    private int price;

    public Book(String title, int price) {
        this.title = title;
        this.price = price;
    }

    @Override
    public int compareTo(Object o) {
        if (o != null && o instanceof Book) {
            Book b = (Book) o;
            return title.compareTo(b.title);
        }
        else
            return -1;
    }

    public String toString() {
        return MessageFormat.format("제목 = {0}, 가격 = {1}", title, price);
    }
}

 

연습문제 03)

class Book implements Comparable {
    private String title;
    private int price;

    public Book(String title, int price) {
        this.title = title;
        this.price = price;
    }

    @Override
    public int compareTo(Object o) {
        if (o != null && o instanceof Book) {
            Book b = (Book) o;
            if (price > b.price)
                return 1;
            else if (price < b.price)
                return -1;
            else
                return 0;
        }
        else
            return -1;
    }

    public String toString() {
        return MessageFormat.format("제목 = {0}, 가격 = {1}", title, price);
    }
}

'Programming > Java' 카테고리의 다른 글

자바 프로그래밍 - 9  (0) 2023.06.20
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함