Q1. 클래스 상속하고 메서드 추가하기

Calculator 클래스를 상속한 UpgradeCalculator를 정의하고, value 속성에서 매개변수 val에 전달된 값을 빼는 minus 메서드를 구현하였다.

// Sample.java
 
class Calculator {  
    int value;  
  
    Calculator() {  
        this.value = 0;  
    }  
  
    void add(int val) {  
        this.value += val;  
    }  
  
    int getValue() {  
        return this.value;  
    }  
}  
  
class UpgradeCalculator extends Calculator {  
    void minus(int val) {  
        this.value -= val;  
    }  
}  
  
public class Sample {  
    public static void main(String[] args) {  
        UpgradeCalculator cal = new UpgradeCalculator();  
        cal.add(10);  
        cal.minus(3);  
        System.out.println(cal.getValue()); // 7 출력  
    }  
}

Q2. 클래스 상속하고 메서드 오버라이딩하기

Calculator 클래스를 상속한 MaxLimitCalculator 클래스를 정의하고, add 메서드를 오버라이딩하여 value 속성이 100을 넘지 않도록 수정하였다. 메서드 오버라이딩을 통해 다형성이 구현현한다.

// Sample.java
 
class Calculator {  
    int value;  
  
    Calculator() {  
        this.value = 0;  
    }  
  
    void add(int val) {  
        this.value += val;  
    }  
  
    int getValue() {  
        return this.value;  
    }  
}  
  
class MaxLimitCalculator extends Calculator {  
    @Override  
    void add(int val) {  
        this.value = Math.min(this.getValue() + val, 100);  
    }  
}  
  
public class Sample {  
    public static void main(String[] args) {  
        MaxLimitCalculator cal = new MaxLimitCalculator();  
        cal.add(50);  
        cal.add(60);  
        System.out.println(cal.getValue()); // 100 출력  
    }  
}

Q3. 홀수/짝수 판별하기 2

홀수면 true, 짝수면 false를 반환하는 isOdd 메서드를 가진 Calculator 클래스를 정의하였다.

// Sample.java
 
class Calculator {
    boolean isOdd(int val) {
        return val % 2 != 0;
    }
}
 
public class Sample {
    public static void main(String[] args) {
        Calculator cal = new Calculator();
        System.out.println(cal.isOdd(3)); // true 출력
        System.out.println(cal.isOdd(4)); // false 출력
    }
}

Q4. 메서드 오버로딩하기

Calculator 클래스는 평균을 구하는 두 종류의 avg 메서드를 갖는다. 두 avg 메서드는 매개변수의 타입이 달라 메서드 오버로딩을 발생시킨다.

// Sample.java
 
import java.util.ArrayList;
import java.util.Arrays;
 
class Calculator {
    int avg(int[] data) {
        int sum = 0;
 
        for (int datum: data) {
            sum += datum;
        }
 
        return sum / data.length;
    }
 
    int avg(ArrayList<Integer> data) {
        int sum = 0;
 
        for (int datum: data) {
            sum += datum;
        }
 
        return sum / data.size();
    }
}
 
public class Sample {
    public static void main(String[] args) {
        Calculator cal = new Calculator();
 
        int[] dataOne = {1, 3, 5, 7, 9};
        int resultOne = cal.avg(dataOne);
        System.out.println(resultOne); // 5 출력
 
        ArrayList<Integer> dataTwo = new ArrayList<>(Arrays.asList(1, 3, 5, 7, 9));
        int resultTwo = cal.avg(dataTwo);
        System.out.println(resultTwo); // 5 출력
 
    }
}

Q5. 리스트와 객체 확인하기

변수 a와 변수 b에는 각각 새롭게 생성된 ArrayList의 인스턴스가 할당되었다. 두 인스턴스는 서로 다른 메모리 공간에 저장되며 상태가 공유되지 않는다. 따라서 a에 요소를 추가해도 b에 영향을 주지 않으며, ab의 참조는 같지 않다.

// Sample.java
 
import java.util.ArrayList;
import java.util.Arrays;
 
public class Sample {
    public static void main(String[] args) {
        ArrayList<Integer> a = new ArrayList<>(Arrays.asList(1, 2, 3));
        ArrayList<Integer> b = new ArrayList<>(a);
        a.add(4);
        System.out.println(b.size()); // 3 출력
        System.out.println(a == b); // false 출력
    }
}

Q6. 생성자와 초깃값 설정하기

Integer value 속성이 초기화되지 않아 초기값 null을 가지게 되고, add 메서드에서 null에 대해 산술 연산을 수행할 때 NullPointerException이 발생한다. value 속성의 초기값을 0으로 할당하는 생성자를 정의하여 문제를 해결한다.

// Sample.java
 
class Calculator {
    Integer value;
 
    Calculator() {
        this.value = 0;
    }
 
    void add(int val) {
        this.value += val;
    }
 
    public Integer getValue() {
        return this.value;
    }
}
 
public class Sample {
    public static void main(String[] args) {
        Calculator cal = new Calculator();
        cal.add(3);
        System.out.println(cal.getValue()); // 3 출력
    }
}
 

Q7. 인터페이스 사용하기

광물의 가치를 반환하는 getValue 메서드를 가진 Mineral 인터페이스를 선언했다. Gold 클래스, Silver 클래스, Bronze 클래스는 Mineral 인터페이스를 구현한다. 이 변경으로 인해 새로운 광물 클래스를 생성해도 MineralCalculator 클래스에 분기문을 추가할 필요가 없어진다.

// Sample.java
 
interface Mineral {  
    int getValue();  
}  
  
class Gold implements Mineral {  
    @Override  
    public int getValue() {  
        return 100;  
    }  
}  
  
class Silver implements Mineral {  
    @Override  
    public int getValue() {  
        return 90;  
    }  
}  
  
class Bronze implements Mineral {  
    @Override  
    public int getValue() {  
        return 80;  
    }  
}  
  
class MineralCalculator {  
    int value = 0;  
  
    public void add(Mineral mineral) {  
        this.value += mineral.getValue();  
    }  
  
    public int getValue() {  
        return this.value;  
    }  
}  
  
public class Sample {  
    public static void main(String[] args) {  
        MineralCalculator cal = new MineralCalculator();  
        cal.add(new Gold());  
        cal.add(new Silver());  
        cal.add(new Bronze());  
        System.out.println(cal.getValue());  // 270 출력  
    }  
}

Q8. 오류 찾기

Dog d = new Animal(); 문장에서 오류가 발생한다.

두 타입이 IS-A 관계에 있을 때, 자식 클래스의 인스턴스는 부모 클래스의 자료형인 것처럼 사용할 수 있다. 그러나 반대의 경우는 성립하지 않는다.

Dog 클래스는 Animal 클래스의 파생 클래스다. 따라서 Dog 인스턴스를 Animal 자료형인 것처럼 사용할 수 있으나, 문제의 문장은 Animal 인스턴스를 Dog 자료형인 것처럼 사용하고 있으므로 오류가 발생한다.

// Sample.java
 
interface Predator {  
}  
  
class Animal {  
}  
  
class Dog extends Animal {  
}  
  
class Lion extends Animal implements Predator {  
}  
  
public class Sample {  
    public static void main(String[] args) {  
        Animal a = new Animal();
        Animal b = new Dog(); 
        Animal c = new Lion();  
        Dog d = new Animal();  // 오류 발생  
        Predator e = new Lion();
    }  
}

Q9. 오류 찾기 2

오류가 발생한 문장은 a.bark()c.hello()다.

변수 aLion 인스턴스를 참조하지만, 참조 변수의 타입은 Animal이다. Animal 클래스에는 bark 메서드가 정의되지 않았기 때문에 참조 변수 타입에 의한 오류가 발생한다.

변수 cLion 인스턴스를 참조하지만, 참조 변수의 타입은 Predator다. Predator 인터페이스에는 hello 메서드가 정의되지 않았기 때문에 참조 변수 타입에 의한 오류가 발생한다.

// Sample.java
 
interface Predator {
    String bark();
}
 
abstract class Animal {
    public String hello() {
        return "hello";
    }
}
 
class Dog extends Animal {
}
 
class Lion extends Animal implements Predator {
    public String bark() {
        return "Bark bark!!";
    }
}
 
public class Sample {
    public static void main(String[] args) {
        Animal a = new Lion();
        Lion b = new Lion();
        Predator c = new Lion();
 
        System.out.println(a.hello());
        System.out.println(a.bark());   // 오류 발생
        System.out.println(b.hello());
        System.out.println(b.bark());
        System.out.println(c.hello());  // 오류 발생
        System.out.println(c.bark());
    }
}