상속(프로그래밍 심화)(명품 JAVA Programming )
용어정리
만약 이 글을 보고 자바의 기능을 깨달으려 한다면 뒤로가기를 누르시면 됩니다. 코딩은 무조건 직접 해봐야 깨닫는 것 같아요. 저는 이하의 기능을 3일 동안 계속해보며 익혔어요. 그래도 정확히 모르겠거든요... 그런 제가 이 글을 작성했으니 더욱 신뢰성이 떨어집니다. 기능을 깨달으시려한다면 뒤로가기를 누르세요!!
본문정리
상속의 개념
객체 지향에서 상속은 부모 클래스에 만들어진 필드와 메소드를 자식 클래스가 물려받는 것이다. 코드 중복을 제거하여 클래스를 간결하게 구현할 수 있다. 상속의 장점은 클래스 간결화(멤버의 중복 작성 불필요), 클래스 관리 용이(클래스들의 계층적 분류), 소프트웨어의 생산성 향상(클래스 재사용과 확장 용이)이다.
클래스 상속과 객체
부모 클래스는 슈퍼 클래스, 자식 클래스는 서브 클래스라 하며 상속을 선언할 때는 extends 키워드를 사용한다. 서브클레스에서 슈퍼클래스의 private 속성을 사용할 수 없음을 알아야한다. 이하는 자바의 상속과 관련한 특징이다.
클래스의 다중 상속을 지원하지 않는다, 상속의 횟수에 제한을 두지 않는다, 계층 구조의 최상위에 java.lang.Object 클래스가 있다.
상속과 protected 접근 지정자
private | 디폴트 | protected | public | |
같은 패키지에 있는 클래스 | X | 0 | 0 | 0 |
다른 패키지에 있는 클래스 | X | X | X | 0 |
같은 패키지에 있는 서브 클래스 | X | 0 | 0 | 0 |
다른 패키지에 있는 서브 클래스 | X | X | 0 | 0 |
상속과 생성자
서브클래스와 슈퍼클래스의 생성자가 모두 생기는데, 서브 클래스 생성자가 실행되면 슈퍼 클래스의 생성자가 실행된 후 서브 클래스 생성자가 실행된다. 이유는 서브 클래스가 슈퍼 클래스 생성자를 호출하는데 호출하게 되면 해당 생성자를 실행하는데 실행한 후 서브 클래스의 생성자를 실행하기 때문이다.
여기서 슈퍼 클래스에 생성자가 다양할 경우, 개발자가 지정을 하지 않으면 기본 생성자를 호출한다. 지정을 하지 않았을 때, 슈퍼 클래스에 기본 생성자가 없다면 오류가 발생한다. 지정하는 방식은 super를 쓰는 것이다. super는 반드시 생성자의 첫 라인에 사용되어야한다.
업캐스팅과 instanceof 연산자
캐스팅이란 타입 변환을 말한다.
서브 클래스의 객체에 대한 레퍼런스를 슈퍼 클래스 타입으로 변환하는 것을 업캐스팅이라 한다. 즉, 슈퍼 클래스의 레퍼런스로 서브 클래스의 객체를 가리키는 것이다. 업캐스팅한 레퍼런스로는 객체 내에 모든 멤버에 접근할 수 없고, 슈퍼 클래스의 멤버만 접근할 수 있다.
다운캐스팅은 명시적으로 타입 변환을 지정해야한다.
레퍼런스 instanceof 클래스명은 레퍼런스가 가리키는 객체가 어떤 클래스 타입인지 구분할 수 있게해주는 이항 연산자이다.
착각을 했었다. 음 글로 내가 착각한 내용을 설명하긴 어렵고 쉽게 생각하는 법을 배웠다. 단순히 '가리킨다'로 해석하면 된다. 상속받은 클래스가 선언되면 당연히 상속한 클래스의 내용도 정의 되어 있을 것이다. 이를 가리키는게 업캐스팅이다. 반면 상속한 캐스팅이 있을 텐데 이는 여러 클래스에 상속이 가능하다. 그렇기 때문에 명시를 해야한다.
메소드 오버라이딩
슈퍼 클래스와 서브 클래스의 메소드 사이에 발생하는 관계로서, 슈퍼 클래스의 메소드를 서브 클래스에서 재작성하는 것이다.(메소드 이름, 같은 리턴 타입, 같은 매개변수 리스트를 가짐) 오버라이딩시 서브 클래스에서 메소드가 무조건 실행되는데 이를 동적 바인딩이라고도 부른다.
하나의 인터페이스에 서로 다른 내용 구현이라는 객체 지향의 다형성을 실현하는 도구로서 해당 기능을 사용한다.
@Override라는 기능으로 오버라딩이 정확한지 확인하도록 지시한다.
슈퍼 클래스의 메소드와 동일한 원형으로 작성하여야 하며, 슈퍼 클래스 메소드의 접근 지정자보다 접근의 범위를 좁혀 오버라이딩 할 수 없다. 또한 static이나 private 또는 final로 선언된 메소드는 서브 클래스에서 오버라이딩할 수 없다.
오버라이딩된 메소드가 있다면 동적 바인딩을 통해 오버라이딩된 메소드가 무조건 실행된다.
super키워드를 사용하면 정적 바인딩을 통해 슈퍼 클래스의 멤버에 접근 할 수 있다.(예시로 super() 혹은 super.메서드명())
오버로딩과 오버라이딩은 아예 다른 개념이다. 전자는 메소드 이름이 같지만 매개변수 타입이나 개수가 다르며, 후자는 메소드 이름·타입·개수·리턴 타입이 모두 동일해야 한다.
추상 클래스
추상 메소드란 선언은 되어 있으나 코드가 구현되어 있지 않은, 즉 껍데기만 있는 메소드이다. abstract란 키워드를 이용한다. 주의할 점은 메서드명만 작성하고 해당 코드는 작성하지 않는다. 추상 메서드를 가지고 있다면 반드시 추상 클래스로 선언되어야 한다. 추상 클래스를 상속 받았을 땐, 추상 메서드에 대해 반드시 오버라이딩해야한다.(abstract가 아닌 메서드는 선언 안해도 된다.) (클래스이기 때문에 추상 메서드를 제외해선 {}를 붙여도 된다. 또한 {} 내용을 작성해도 된다.)
인터페이스
추상 메소드는 public abstract로 정해져 있으며, default· private· static 메소드는 인터페이스 내에 코드가 작성되어야한다. 여기서 default 메소드의 접근 지정은 public으로 고정되어 있다. interface · implements란 키워드를 이용한다. 인터페이스를 상속 받았을 땐, 모든 메서드에 대해 반드시 오버라이딩해야한다. ({}를 어디에도 치면 안된다!)
특별한건 아니지만 나한텐 특별한걸 배웠다. 인터페이스 상속은 extends를 통해서만 가능하다. 이때 상속받았다고 해당 메소드를 오버라이딩하면 안된다. 왜? 인터페이스니까, implements는 class에서만 가능하고 이 때 해당 메소드를 오버라이딩 해야만 한다.
실습문제
Q1. 다음 TV 클래스가 있다.
class TV{
private int size;
public TV(int size) { this.size = size; }
protected int getSize() { return size; }
}
다음 main() 메소드와 실행 결과를 참고하여 TV를 상속받은 ColorTV 클래스를 작성하라.
public static void main(String[] args) {
ColorTV myTV = new ColorTV(32, 1024);
myTV.printProperty();
}
32인치 1024컬러
public class ColorTV extends TV {
int color;
public ColorTV(int x, int y) {
super(x);
this.color = y;
}
public void printProperty() {
System.out.print(getSize() + "인치 " + color +"컬러");
}
public static void main(String[] args) {
ColorTV myTV = new ColorTV(32, 1024);
myTV.printProperty();
}
}
Q2.다음 TV 클래스가 있다.
class TV{
private int size;
public TV(int size) { this.size = size; }
protected int getSize() { return size; }
}
다음 main() 메소드와 실행 결과를 참고하여 ColorTV를 상속받는 IPTV 클래스를 작성하라.
public static void main(String[] args) {
IPTV iptv = new IPTV("192.1.1.2", 32, 2048); //"192.1.1.2" 주소에 32인치, 2048컬러
iptv.printProperty();
}
나의 IPTV는 192.1.1.2 주소의 32인치 2048컬러
class ColorTV extends TV {
private int color;
public ColorTV(int x, int y) {
super(x);
this.color = y;
}
protected int getcolor() {
return color;
}
}
public class IPTV extends ColorTV{
public String a;
public IPTV(String a, int size, int color) {
super(size, color);
this.a = a;
}
public void printProperty() {
System.out.print("나의 IPTV는 "+ a + "주소의 " + getSize() +"인치 " + getcolor() + "컬러" );
}
public static void main(String[] args) {
IPTV iptv = new IPTV("192.1.1.2", 32, 2048); //"192.1.1.2" 주소에 32인치, 2048컬러
iptv.printProperty();
}
}
Q3. 다음은 단위를 변환하는 추상 클래스 Converter이다.
import java.util.Scanner;
abstract class Converter {
abstract protected double convert(double src); // 추상 메소드
abstract protected String getSrcString(); // 추상 메소드
abstract protected String getDestString(); // 추상 메소드
protected double ratio; // 비율
public void run() {
Scanner scanner = new Scanner(System.in);
System.out.println(getSrcString()+"을 "+getDestString()+"로 바꿉니다.");
System.out.print(getSrcString()+"을 입력하세요>> ");
double val = scanner.nextDouble();
double res = convert(val);
System.out.println("변환 결과: "+res+getDestString()+"입니다");
scanner.close();
}
}
Converter 클래스를 상속받아 원화를 달러로 변환하는 Won2Dollar 클래스를 작성하라. main() 메소드와 실행 결과는 다음과 같다.
public static void main(String args[]) {
Won2Dollar toDollar = new Won2Dollar(1200); // 1달러는 1200원
toDollar.run();
}
원을 달러로 바꿉니다.
원을 입력하세요>> 24000
변환 결과: 20.0달러입니다
public class Won2Dollar extends Converter{
public Won2Dollar(int ratio) {
this.ratio = ratio;
}
protected double convert(double src) {
return src/this.ratio;
}
protected String getSrcString() {
return "원";
}
protected String getDestString() {
return "달러";
}
public static void main(String[] args) {
Won2Dollar toDollar = new Won2Dollar(1200); // 1달러는 1200원
toDollar.run();
}
}
Q4.다음은 단위를 변환하는 추상 클래스 Converter이다.
import java.util.Scanner;
abstract class Converter {
abstract protected double convert(double src); // 추상 메소드
abstract protected String getSrcString(); // 추상 메소드
abstract protected String getDestString(); // 추상 메소드
protected double ratio; // 비율
public void run() {
Scanner scanner = new Scanner(System.in);
System.out.println(getSrcString()+"을 "+getDestString()+"로 바꿉니다.");
System.out.print(getSrcString()+"을 입력하세요>> ");
double val = scanner.nextDouble();
double res = convert(val);
System.out.println("변환 결과: "+res+getDestString()+"입니다");
scanner.close();
}
}
Converter 클래스를 상속받아 Km를 mile(마일)로 변환하는 Km2Mile 클래스를 작성하라, main() 메소드와 실행 결과는 다음과 같다.
public static void main(String args[]) {
Km2Mile toMile = new Km2Mile(1.6); // 1마일은 1.6km
toMile.run();
}
Km을 mile로 바꿉니다.
Km을 입력하세요>> 30
변환 결과: 18.75mile입니다
public class Km2Mile extends Converter {
public Km2Mile(double mile) {
this.ratio = mile;
}
protected double convert(double src) {
return src / this.ratio;
}
protected String getSrcString() {
return "Km";
}
protected String getDestString() {
return "mile";
}
public static void main(String[] args) {
Km2Mile toMile = new Km2Mile(1.6); // 1마일은 1.6km
toMile.run();
}
}
Q5. 다음은 2차원 상의 한 점을 표현하는 Point 클래스이다.
class Point {
private int x, y;
public Point(int x, int y) { this.x = x; this.y = y; }
public int getX() { return x; }
public int getY() { return y; }
protected void move(int x, int y) { this.x =x; this.y = y; }
}
Point를 상속받아 색을 가진 점을 나타내는 ColorPoint 클래스를 작성하라. 다음 main() 메소드를 포함하고 실행 결과와 같이 출력되게 하라.
public static void main(String[] args) {
ColorPoint cp = new ColorPoint(5, 5, "YELLOW");
cp.setXY(10, 20);
cp.setColor("RED");
String str = cp.toString();
System.out.println(str+"입니다. ");
}
RED색의 (10,20)의 점입니다.
public class ColorPoint extends Point {
String color;
int x, y;
public ColorPoint(int x, int y, String color) {
super(x,y);
this.color = color;
}
public void setXY(int x, int y) {
this.x = x;
this.y = y;
}
public void setColor(String color) {
this.color = color;
}
public String toString() {
return this.color + "색의 (" + this.x + ","+ this.y +")의 점입니다.";
}
public static void main(String[] args) {
ColorPoint cp = new ColorPoint(5, 5, "YELLOW");
cp.setXY(10, 20);
cp.setColor("RED");
String str = cp.toString();
System.out.println(str+"입니다. ");
}
}
Q6. 다음은 2차원 상의 한 점을 표현하는 Point 클래스이다.
class Point {
private int x, y;
public Point(int x, int y) { this.x = x; this.y = y; }
public int getX() { return x; }
public int getY() { return y; }
protected void move(int x, int y) { this.x =x; this.y = y; }
}
Point를 상속받아 색을 가진 점을 나타내는 ColorPoint 클래스를 작성하라. 다음 main() 메소드를 포함하고 실행 결과와 같이 출력되게 하라.
public static void main(String[] args) {
ColorPoint zeroPoint = new ColorPoint(); // (0,0) 위치의 BLACK 색 점
System.out.println(zeroPoint.toString() + "입니다.");
ColorPoint cp = new ColorPoint(10, 10); // (10,10) 위치의 BLACK 색 점
cp.setXY(5,5);
cp.setColor("RED");
System.out.println(cp.toString()+"입니다.");
}
BLACK색의 (0,0) 점입니다.
RED색의 (5,5) 점입니다.
public class ColorPoint extends Point {
String color;
int x, y;
public ColorPoint() {
super(0,0);
this.color = "BLACK";
}
public ColorPoint(int x, int y) {
super(x,y);
this.color = "BLACK";
}
public ColorPoint(int x, int y, String color) {
super(x,y);
this.color = color;
}
public void setXY(int x, int y) {
this.x = x;
this.y = y;
}
public void setColor(String color) {
this.color = color;
}
public String toString() {
return this.color + "색의 (" + this.x + ","+ this.y +")의 점";
}
public static void main(String[] args) {
ColorPoint zeroPoint = new ColorPoint(); // (0,0) 위치의 BLACK 색 점
System.out.println(zeroPoint.toString() + "입니다.");
ColorPoint cp = new ColorPoint(10, 10); // (10,10) 위치의 BLACK 색 점
cp.setXY(5,5);
cp.setColor("RED");
System.out.println(cp.toString()+"입니다.");
}
}
Q7. 다음은 2차원 상의 한 점을 표현하는 Point 클래스이다.
class Point {
private int x, y;
public Point(int x, int y) { this.x = x; this.y = y; }
public int getX() { return x; }
public int getY() { return y; }
protected void move(int x, int y) { this.x =x; this.y = y; }
}
Point를 상속받아 3차원의 점을 나타내는 Point3D 클래스를 작성하라. 다음 main() 메소드를 포함하고 실행 결과와 같이 출력되게 하라.
public static void main(String[] args) {
Point3D p = new Point3D(1,2,3); // 1,2,3은 각각 x, y, z축의 값.
System.out.println(p.toString()+"입니다.");
p.moveUp(); // z 축으로 위쪽 이동
System.out.println(p.toString()+"입니다.");
p.moveDown(); // z 축으로 아래쪽 이동
p.move(10, 10); // x, y 축으로 이동
System.out.println(p.toString()+"입니다.");
p.move(100, 200, 300); // x, y, z축으로 이동
System.out.println(p.toString()+"입니다.");
}
(1,2,3) 의 점입니다.
(1,2,4) 의 점입니다.
(10,10,3) 의 점입니다.
(100,200,300) 의 점입니다.
public class Point3D extends Point {
int z;
public Point3D(int x, int y, int z) {
super(x,y);
this.z = z;
}
public void moveUp() {
this.z += 1;
}
public void moveDown() {
this.z -= 1;
}
public void move(int x, int y, int z) {
super.move(x, y);
this.z = z;
}
public String toString() {
return "(" + getX() + "," + getY() + "," + this.z + ")" + "의 점";
}
public static void main(String[] args) {
Point3D p = new Point3D(1,2,3); // 1,2,3은 각각 x, y, z축의 값.
System.out.println(p.toString()+"입니다.");
p.moveUp(); // z 축으로 위쪽 이동
System.out.println(p.toString()+"입니다.");
p.moveDown(); // z 축으로 아래쪽 이동
p.move(10, 10); // x, y 축으로 이동
System.out.println(p.toString()+"입니다.");
p.move(100, 200, 300); // x, y, z축으로 이동
System.out.println(p.toString()+"입니다.");
}
}
Q8. 다음은 2차원 상의 한 점을 표현하는 Point 클래스이다.
class Point {
private int x, y;
public Point(int x, int y) { this.x = x; this.y = y; }
public int getX() { return x; }
public int getY() { return y; }
protected void move(int x, int y) { this.x =x; this.y = y; }
}
Point를 상속받아 양수의 공간에서만 점을 나타내는 PositivePoint 클래스를 작성하라. 다음 main() 메소드를 포함하고 실행 결과와 같이 출력되게 하라.
public static void main(String[] args) {
PositivePoint p = new PositivePoitnt();
p.move(10, 10);
System.out.println(p.toString()+"입니다.");
p.move(-5,5); // 객체 p는 음수 공간으로 이동되지 않음
System.out.println(p.toString()+"입니다.");
PositivePoint p2 = new PositivePoint(-10, -10);
System.out.println(p2.toString()+"입니다.");
}
(10,10)의 점입니다.
(10,10)의 점입니다.
(0,0)의 점입니다.
이하의 정답에서 클래스 명을 PositivePoint로 바꿨다.( 이걸로 한시간 고민함 ㅎ)
public class PositivePoint extends Point {
public PositivePoint() {
super(0,0);
}
public PositivePoint(int x, int y) {
super(x,y);
if(x<0||y<0)
super.move(0, 0);
}
public void move(int x, int y) {
if(x>=0 && y>=0)
super.move(x,y);
}
public String toString() {
return "(" + getX() + "," + getY() + ")의 점";
}
public static void main(String[] args) {
PositivePoint p = new PositivePoint();
p.move(10, 10);
System.out.println(p.toString()+"입니다.");
p.move(-5,5); // 객체 p는 음수 공간으로 이동되지 않음
System.out.println(p.toString()+"입니다.");
PositivePoint p2 = new PositivePoint(-10, -10);
System.out.println(p2.toString()+"입니다.");
}
}
Q9. 다음 Stack 인터페이스를 상속받아 실수를 저장하는 StringStack 클래스를 구현하라.
interface Stack {
int length(); // 현재 스택에 저장된 개수 리턴
int capacity(); // 스택의 전체 저장 가능한 개수 리턴
String pop(); // 스택의 톱(top)에 실수 저장
boolean push(String val); // 스택의 톱(top)에 저장된 실수 리턴
}
그리고 다음 실행 사례와 같이 작동하도록 StackApp 클래스에 main() 메소드를 작성하라.
총 스택 저장 공간의 크기 입력 >> 3
문자열 입력 >> hello
문자열 입력 >> sunny
문자열 입력 >> smile
문자열 입력 >> happy
스택이 꽉 차서 푸시 불가!
문자열 입력 >> 그만
스택에 저장된 모든 문자열 팝 : smile sunny hello
public class StringStack implements Stack{
private int len;
private int size;
private String[] stack;
public StringStack() {}
public StringStack(int size) {
this.size = size;
this.len = 0;
stack = new String[size];
}
public int length() {
return len;
}
public int capacity() {
return size;
}
public String pop() {
if(len - 1 < 0)
return null;
else {
len--;
return stack[len];
}
}
public boolean push(String val) {
if(len <size) {
stack[len] = val;
len++;
return true;
}
else {
return false;
}
}
public static void main(String[] args) {
int size;
String str;
Scanner sc = new Scanner(System.in);
System.out.print("총 스택 저장 공간의 크기 입력 >> ");
size = sc.nextInt();
StringStack k = new StringStack(size);
while(true) {
System.out.print("문자열 입력 >> ");
str = sc.next();
if(str.equals("그만"))
break;
else if(!k.push(str)){
System.out.println("스택이 꽉 차서 푸시 불가!");
}
}
System.out.print("스택에 저장된 모든 문자열 팝 : ");
for(int i = 0; i < size ; i++)
System.out.print(k.pop() + " ");
}
}
Q10. 다음은 키와 값을 하나의 아이템으로 저장하고 검색 수정이 가능한 추상 클래스가 있다.
abstract class PairMap {
protected String keyArray[]; // key 들을 저장하는 배열
protected String valueArray[]; // value 들을 저장하는 배열
abstract String get(String key); // key 값을 가진 value 리턴, 없으면 null 리턴
abstract void put(String key, String value); // key와 value를 쌍으로 저장. 기존에 key가 있으면, 값을 value로 수정
abstract String delete(String key); // key 값을 가진 아이템 (value와 함꼐) 삭제, 삭제된 value 값 리턴
abstract int length(); // 현재 저장된 아이템의 개수 리턴
}
PairMap을 상속받는 Dictionary 클래스를 구현하고, 이를 다음과 같이 활용하는 main() 메소드를 가진 클래스 DictionaryApp도 작성하라.
public static void main(String[] args) {
Dictionary dic = new Dictionary(10);
dic.put("황기태", "자바");
dic.put("이재문", "파이선");
dic.put("이재문", "C++"); // 이재문의 값을 C++로 수정
System.out.println("이재문의 값은 "+dic.get("이재문"));
System.out.println("황기태의 값은 "+dic.get("황기태"));
dic.delete("황기태"); // 황기태 아이템 삭제
System.out.println("황기태의 값은 "+dic.get("황기태")); //삭제된 아이템 접근
}
이재문의 값은 C++
황기태의 값은 자바
황기태의 값은 null
package 명품자바;
abstract class PairMap {
protected String keyArray[]; // key 들을 저장하는 배열
protected String valueArray[]; // value 들을 저장하는 배열
abstract String get(String key); // key 값을 가진 value 리턴, 없으면 null 리턴
abstract void put(String key, String value); // key와 value를 쌍으로 저장. 기존에 key가 있으면, 값을 value로 수정
abstract String delete(String key); // key 값을 가진 아이템 (value와 함꼐) 삭제, 삭제된 value 값 리턴
abstract int length(); // 현재 저장된 아이템의 개수 리턴
}
class Dictionary extends PairMap{
int cur = 0;
String a = null;
public Dictionary(int a) {
keyArray = new String[a];
valueArray = new String[a];
}
public void put(String key, String value) {
for(int i = 0; i < keyArray.length ; i++) {
if(key.equals(keyArray[i])) {
valueArray[i] = value;
return;
}
}
keyArray[cur] = key;
valueArray[cur] = value;
cur++;
return;
}
public String get(String key) {
for(int i = 0; i < keyArray.length; i++) {
if(key.equals(keyArray[i])) {
return valueArray[i];
}
}
return null;
}
public String delete(String key) {
for(int i = 0; i<keyArray.length; i++) {
if(key.equals(keyArray[i])) {
a = valueArray[i];
valueArray[i] = null;
keyArray[i] = null;
return a;
}
}
return null;
}
public int length() {
return cur;
}
}
public class DictionaryApp {
public static void main(String[] args) {
Dictionary dic = new Dictionary(10);
dic.put("황기태", "자바");
dic.put("이재문", "파이선");
dic.put("이재문", "C++"); // 이재문의 값을 C++로 수정
System.out.println("이재문의 값은 "+dic.get("이재문"));
System.out.println("황기태의 값은 "+dic.get("황기태"));
dic.delete("황기태"); // 황기태 아이템 삭제
System.out.println("황기태의 값은 "+dic.get("황기태")); //삭제된 아이템 접근
System.out.println(dic.length());
}
}
Q11. 철수 학생은 다음 3개의 필드와 메소드를 가진 4개의 클래스 Add, Sub, Mul, Div를 작성하려고 한다(4장 실습문제 11 참고).
- int 타입의 a, b 필드: 2개의 피연산자
- void setValue(int a, int b): 피연산자 값을 객체 내에 저장한다.
- int calculate(): 클래스의 목적에 맞는 연산을 실행하고 결과를 리턴한다.
곰곰 생각해보니, Add, Sub, Mul, Div 클래스에 공통된 필드와 메소드가 존재하므로 새로운 추상 클래스 Calc를 작성하고 Calc를 상속받아 만들면 되겠다고 생각했다. 그리고 main() 메소드에서 다음 실행 사례와 같이 2개의 정수와 연산자를 입력받은 후, Add, Sub, Mul, Div 중에서 이 연산을 처리할 수 있는 객체를 생성하고 setValue() 와 calculate()를 호출하여 그 결과 값을 화면에 출력하면 된다고 생각하였다. 철수처럼 프로그램을 작성하라.
두 정수와 연산자를 입력하시오 >> 5 7 +
12
package 명품자바;
import java.util.Scanner;
abstract class Calc{
int a,b;
public void setValue(int a, int b) {
this.a = a; this.b = b;
}
abstract int calculate();
}
class Add extends Calc{
public int calculate() {
return a+b;
}
}
class Sub extends Calc{
public int calculate() {
return a-b;
}
}
class Mul extends Calc{
public int calculate() {
return a*b;
}
}
class Div extends Calc{
public int calculate() {
return a/b;
}
}
public class qwer {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a, b;
String c;
System.out.print("두 정수와 연산자 입력");
a = sc.nextInt();
b = sc.nextInt();
c = sc.next();
Calc k = null;
if(c.equals("+")) {
k =new Add();
}
else if(c.equals("-")) {
k =new Sub();
}
else if(c.equals("/")) {
k =new Div();
}
else if(c.equals("*")) {
k =new Mul();
}
k.setValue(a, b);
System.out.print(k.calculate());
}
}
막 화가 가는게 중간에 Calc k를 초기화 안했더니 컴파일 에러가 났다. 진짜 2시간 고민했는데 이게 뭐가 문제일까 다른건 초기화 안해도 문제 없던 왜 저것만?
Q12. 텍스트로 입출력하는 간단한 그래픽 편집기를 만들어보자. 본문 5.6절과 5.7절에서 사례로 든 추상 클래스 Shape과 Line, Rect, Circle 클래스 코드를 잘 완성하고 이를 활용하여 아래 시행 예시처럼 "삽입", "삭제", "모두 보기", "종료" 의 4가지 그래픽 편집 기능을 가진 클래스 GraphicEditor을 작성하라.
그래픽 에디터 beauty를 실행합니다.
삽입(1), 삭제(2), 모두 보기(3), 종료(4) >> 1
Line(1), Rect(2), Circle(3) >> 2
삽입(1), 삭제(2), 모두 보기(3), 종료(4) >> 1
Line(1), Rect(2), Circle(3) >> 3
삽입(1), 삭제(2), 모두 보기(3), 종료(4) >> 3
Rect
Circle
삽입(1), 삭제(2), 모두 보기(3), 종료(4) >> 2
삭제할 도형의 위치 >> 3
삭제할 수 없습니다.
삽입(1), 삭제(2), 모두 보기(3), 종료(4) >> 4
beauty을 종료합니다.
[Hint] Shape을 추상 클래스로 작성한 사례는 다음과 같다.
abstract class Shape {
private Shape next;
public Shape() { next = null; }
public void setNext(Shape obj) { next = obj; } // 링크 연결
public Shape getNext() { return next; }
public abstract void draw();
}
이거 나오면 버린다.
Q13. 다음은 도형의 구성을 묘사하는 인터페이스이다.
interface Shape {
final double PI = 3.14; // 상수
void draw(); // 도형을 그리는 추상 메소드
double getArea(); // 도형의 면적을 리턴하는 추상 메소드
default public void redraw() { // 디폴트 메소드
System.out.print("--- 다시 그립니다.");
draw();
}
}
다음 main() 메소드와 실행 결과를 참고하여, 인터페이스 Shape을 구현한 클래스 Circle를 작성하고 전체 프로그램을 완성하라.
public static void main(String[] args) {
Shape donut = new Circle(10); // 반지름이 10인 원 객체
donut.redraw();
System.out.println("면적은 "+ donut.getArea());
}
class Circle implements Shape{
int r = 0;
public Circle(int a) {
this.r = a;
}
public void draw() {
System.out.println("반지름이 " + this.r + "인 원입니다.");
}
public double getArea() {
return this.r *this.r*PI;
}
}
Q14. 다음 main() 메소드와 실행 결과를 참고하여, 문제 13의 Shape 인터페이스를 구현한 클래스 Oval, Rect를 추가 작성하고 전체 프로그램을 완성하라.
public static void main(String[] args) {
Shape[] list = new Shape[3]; // Shape을 상속받은 클래스 객체의 레퍼런스 배열
list[0] = new Circle(10); // 반지름이 10인 원 객체
list[1] = new Oval(20, 30); // 20x30 사각형에 내접하는 타원
list[2] = new Rect(10, 40); // 10x40 크기의 사각형
for(int i=0; i<list.length; i++) list[i].redraw();
for(int i=0; i<list.length; i++) System.out.println("면적은 "+ list[i].getArea());
}
--- 다시 그립니다.반지름이 10인 원입니다.
--- 다시 그립니다.20x30에 내접하는 타원입니다.
--- 다시 그립니다.10x40크기의 사각형 입니다.
면적은 314.0
면적은 1884.0000000000002
면적은 400.0
class Oval implements Shape{
int a, b;
public Oval(int a, int b) {
this.a = a; this.b = b;
}
public void draw() {
System.out.println(this.a + "*" + this.b +"에 내접하는 원입니다.");
}
public double getArea() {
return PI*a*b;
}
}
class Rect implements Shape{
int a, b;
public Rect(int a, int b) {
this.a = a; this.b = b;
}
public void draw() {
System.out.println(this.a + "*" + this.b +"크기의 사각형 입니다.");
}
public double getArea() {
return a*b;
}
}