Java Programming for Starter(9-1)

Beautify.log·2021년 12월 16일
0

Java 튜토리얼

목록 보기
12/14
post-thumbnail

저번 포스팅에서 정리한 내용을 상기시키며 연습문제를 해결해 보겠습니다. 총 4문제입니다.

문제 1


문제

다음은 도형을 정의한 Shape클래스이다. 이 클래스를 부모로 하는 Circle클래스와 Rectangle클래스를 작성하시오. 이 때, 생성자도 각 클래스에 맞게 적절히 추가해야 한다.

(1) 클래스명 : Circle
부모 클래스 : Shape
멤버 변수 : double r == 반지름

(2) 클래스명 : Rectangle
부모 클래스 : Shape
멤버 변수 : int width = 폭, int height = 높이
메서드 : isSquare라는 이름을 갖고, 정사각형인지 아닌지 알려준다. boolean타입을 반환하고 매개변수는 없음.

다음과 같은 클래스가 주어짐.

// Shape.java

abstract class Shape {
	Point p;
    
	Shape() {
		this(new Point(0,0));
	}

	Shape(Point p) {
		this.p = p;
	}
    
	abstract double calcArea(); // 도형의 면적을 계산해서 반환하는 메서드

	Point getPosition() {
		return p;
	}
    
	void setPosition(Point p) {
		this.p = p;
	}
}
class Point {
	int x;
	int y;

	Point() {
		this(0,0);
	}
    
	Point(int x, int y) {
		this.x=x;
		this.y=y;
	}
    
	public String toString() {
		return "["+x+","+y+"]";
	}
}

해결방법

우선 Shape 클래스를 상속받는 Circle 클래스와 Rectangle 클래스를 만들어줍시다.

// Circle 클래스

public class Circle extends Shape {}
// Rectangle 클래스

public class Rectangle extends Shape {}

그리고 Circle 클래스와 Rectangle 클래스에 각각 변수 double rint width, int height을 선언해줍니다.

// Circle 클래스

public class Circle extends Shape {

	double r;

}
// Rectangle 클래스

public class Rectangle extends Shape {

	int width;
	int height;

}

그 다음은 생성자를 만들어줘야겠죠?

// Circle 클래스

public class Circle extends Shape {

	double r;
    
	public Circle (double r) {
		this.r = r;
	}

}
// Rectangle 클래스

public class Rectangle extends Shape {

	int width;
	int height;
    
	public Rectangle(int width, int height) {
		this.width = width;
		this.height = height;
	}

}

그리고 각각의 클래스에는 도형의 면적을 반환하는 메서드를 추가해줘야 합니다.

Sharp 클래스를 잘 보시면 calcArea()메서드가 추상 메서드로 선언되어 있습니다. 이미 상속을 해줬으니 오버라이딩으로 끌어다 쓰면 되겠죠?

// Circle 클래스

public class Circle extends Shape {

	double r;
    
	public Circle (double r) {
		this.r = r;
	}
    
	@Override
 	public double calcArea() {
		return r * r * Math.PI;
	}

}

원의 넓이는 반지름 x 반지름 x 원주율이기 때문에 반지름으로 선언해준 r에 원주율(Math.PI)을 곱해주어 리턴해줍니다.

// Rectangle 클래스

public class Rectangle extends Shape {

	int width;
	int height;
    
	public Rectangle(int width, int height) {
		this.width = width;
		this.height = height;
	}

	@Override
	public double calcArea() {
		return width * height;
	}

}

마찬가지로 Rectangle 클래스에도 오버라이딩한 calcArea()메서드를 사각형 넓이 구하는 공식을 계산하여 리턴해주게 합니다.

마지막으로 Rectangle 클래스에는 정사각형 여부를 판별해주는 isSquared() 메서드를 선언하라고 나와있습니다.

정사각형은 폭과 높이가 같아야 하므로 조건식을 써서 참, 거짓을 반환하게 해줍니다.

// Rectangle 클래스

public class Rectangle extends Shape {

	int width;
	int height;
    
	public Rectangle(int width, int height) {
		this.width = width;
		this.height = height;
	}

	@Override
	public double calcArea() {
		return width * height;
	}
    
	public boolean isSquared() {
		return width == height ? true : false;
	}

}

저는 이렇게 삼항 연산자로 써주었습니다. if문을 쓴다고 한다면

// Rectangle 클래스

public class Rectangle extends Shape {

	int width;
	int height;
    
	public Rectangle(int width, int height) {
		this.width = width;
		this.height = height;
	}

	@Override
	public double calcArea() {
		return width * height;
	}
    
	public boolean isSquared() {
		if(width == height) {
			return true;
		} else {
			return false;
		}
	}

}

이렇게 작성해 줄 수 있겠죠?

메인 클래스에서 확인해보겠습니다.

// Main.java

public class Main {
	public static void main(String[] args) {
		
		Circle c = new Circle(3.0);			// double 타입, 반지름이 3인 원
		System.out.println(c.calcArea());		// 28.274333882308138
        
		Rectangle ra = new Rectangle(3, 6);		// 폭이 3, 높이가 6인 사각형
		System.out.println(ra.calcArea());		// 18.0
		System.out.println(ra.isSquared());		// false
        
	}
}

문제 2


문제

다음과 같은 실행결과를 얻도록 Point3D클래스의 equals()를 멤버변수인 x, y, z 의 값을 비교하도록 오버라이딩하고, toString()은 실행결과를 참고해서 적절히 오버라이딩하시오.

다음과 같은 클래스가 주어짐.

// Main.java

public class Main {
	public static void main(String[] args) {
    
		Point3D p1 = new Point3D(1,2,3);
		Point3D p2 = new Point3D(1,2,3);

		System.out.println(p1);
		System.out.println(p2);
		System.out.println("p1==p2?"+(p1==p2));
		System.out.println("p1.equals(p2)?"+(p1.equals(p2)));
	}
}
class Point3D {
	int x, y, z;
	public Point3D(int x, int y, int z) {
		this.x=x;
		this.y=y;
		this.z=z;
	}
    
	public Point3D() {
		this(0,0,0);
	}
    
	public boolean equals(Object obj) {
		/*
		(1) 인스턴스변수 x, y, z를 비교하도록 오버라이딩하시오.
		*/
	}
    
	public String toString() {
		/*
		(2) 인스턴스변수 x, y, z의 내용을 출력하도록 오버라이딩하시오.
		*/
	}
}
[실행결과]
[1,2,3]
[1,2,3]
p1==p2?false
p1.equals(p2)?true

해결방법

우선 메인 클래스를 보시면 Point3D라는 클래스로 각각 p1, p2를 인스턴스로 하는 객체를 만들어주었습니다. 이 객체는 매개변수로 세개의 숫자를 받습니다.

그 다음으로 각각 객체를 출력해주고, p1p2가 같은지(==) 비교해주는데 이 비교는 객체의 heap 메모리 주소가 같은지 판별해 주는 것입니다.

그리고 euals() 메서드를 만들어서 객체의 내용이 같은지 비교해주는 것이죠.

Point3D 객체는 내부에서 x, y, z를 매개 변수로 받는 생성자가 있고, 매개변수를 받지 않는 생성자는 x, y, z가 각각 0으로 값이 초기화 되어 있습니다.

그 다음 우리는 equals() 메서드와 toStrind() 메서드를 구현해야 합니다.

우선 equals() 메서드부터 구현해 주겠습니다.

public boolean equals(Object obj) {

}

이 메서드는 파라미터로 obj 객체를 받아옵니다. 이 객체는 Object 즉 자바에서 어떠한 객체의 상속도 받지 않는 최상위 객체입니다.

다시 말해서 어떤 객체를 대입하든 다 들어갈 수 있다는 것이죠!

그리고 equals() 메서드는 이미 우리가 각각 다른 메모리에 저장되어 있는 값의 동일 여부를 조회할 때 사용했었습니다. 마찬가지로 boolean을 리턴해줬죠?

그래서 우리는 내장되어있는 메서드를 오버라이딩해서 우리 입맛에 맞게 바꿔줄겁니다.

@Override
public boolean equals(Object obj) {

}

이 때 equals() 메서드의 역할을 다시 한 번 상기시켜보면 객체 변수 A가 부모객체를 참조하는지 자식객체를 참조하는지에 대해 boolean 값으로 나타내줍니다.

여기서 우리는 p1p2의 각 값이 같은지 비교해 줄 것입니다.

@Override
public boolean equals(Object obj) {
	Point3D p2 = (Point3D)obj;
}

우선 매개변수로 받은 최상위 객체 objPoint3D 객체 형태로 캐스트 변환 시켜줍니다.
그리고 이 안에 들어오는 값이 각각 같으면 true를 반환해 주는 형태로 작성하면 되겠습니다.

@Override
public boolean equals(Object obj) {
	Point3D p2 = (Point3D)obj;
	return x == p2.x && y == p2.y && z == p2.z;
}

이렇게 리턴값을 지정해주면 모두 true일 때만 true를 반환해 주겠지요?

그리고 toString() 메서드는 단순히 x, y, z를 출력 형식에 맞게만 리턴해주면 됩니다.

단, toString() 메서드도 이미 내장되어 있는 메서드이기 때문에 오버라이딩 해주어야 합니다.

@Override
public String toString() {
	return "[" + x + ", " + y + ", " + z + "]"
}

이렇게 해주면 완성입니다.


문제 3


문제

아래 세 개의 클래스로부터 공통부분을 뽑아서 Unit이라는 클래스를 만들고, 이 클래스를 상속받도록 코드를 변경하시오.

class Marine { // 보병
	int x, y; // 현재 위치
	void move(int x, int y) { /* 지정된 위치로 이동 */ }
	void stop() { /* 현재 위치에 정지 */ }
	void stimPack() { /* 스팀팩을 사용한다.*/}
}
class Tank { // 탱크
	int x, y; // 현재 위치
	void move(int x, int y) { /* 지정된 위치로 이동 */ }
	void stop() { /* 현재 위치에 정지 */ }
	void changeMode() { /* 공격모드를 변환한다. */}
}
class Dropship { // 수송선 
	int x, y; // 현재 위치
	void move(int x, int y) { /* 지정된 위치로 이동 */ }
	void stop() { /* 현재 위치에 정지 */ }
	void load() { /* 선택된 대상을 태운다.*/ }
	void unload() { /* 선택된 대상을 내린다.*/ }
}

해결방법

상속은 공통된 요소들끼리 묶어주면 된다고 했습니다. 그러므로 각각의 클래스에서 공통되는 것만 뽑아서 Unit이라는 클래스에 넣어주면 되겠습니다.
int x, yvoid move(int x, int y), void stop() 이 세개의 항목이 공통되므로 Unit이라는 클래스를 만들어 넣어줍시다.

// Unit.java

public class Unit {
	protected int x, y;
	protected void move(int x, int y) {}
	protected void stop() {}
}

그리고 상속을 시켜주기 전에 Unit 클래스에 생성자를 만들어줍시다!

// Unit.java

public class Unit {
	protected int x, y;
	protected void move(int x, int y) {}
	protected void stop() {}
    
	// 생성자
	public Unit(int x, int y) {
		this.x = x;
		this.y = y;
	}
}

그리고 Marine, Tank, Dropship 클래스에 Unit 클래스를 상속시켜 줍니다.

class Marine extends Unit { // 보병

	public void stimPack() { /* 스팀팩을 사용한다.*/}
}
class Tank extends Unit { // 탱크

	public void changeMode() { /* 공격모드를 변환한다. */}
}
class Dropship extends Unit { // 수송선 

	public void load() { /* 선택된 대상을 태운다.*/ }
	public void unload() { /* 선택된 대상을 내린다.*/ }
}

상속받은 메서드를 오버라이딩 해줍시다. 여기에 생성자까지 추가해주면 더 좋겠죠?

class Marine extends Unit { // 보병

	public Marine(int x, int y) {
		super(x, y);
 	}
	public void stimPack() { /* 스팀팩을 사용한다.*/}
    
	@Override
	protected void move(int x, int y) {}
    
	@Override
	protected void stop() {}
}
class Tank extends Unit { // 탱크
	public Tank(int x, int y) {
		super(x, y);
 	}
	public void changeMode() { /* 공격모드를 변환한다. */}
    
	@Override
	protected void move(int x, int y) {}
    
	@Override
	protected void stop() {}
}
class Dropship extends Unit { // 수송선 
	public Dropship(int x, int y) {
		super(x, y);
 	}
	public void load() { /* 선택된 대상을 태운다.*/ }
	public void unload() { /* 선택된 대상을 내린다.*/ }
    
	@Override
	protected void move(int x, int y) {}
    
	@Override
	protected void stop() {}
}

인터페이스를 사용해주는 방법도 있습니다.

다음과 같이 인터페이스를 만들어주고 인터페이스에는 추상 메서드만 들어가므로 추상 메서드를 넣어줍니다.

public interface Unit {
	public void (int x, int y);
	public void stop;
}

그리고 각각의 클래스들은 인터페이스에 변수를 선언할 수 없어서 담아주지 않았기 때문에 자신이 가지고 있어야 합니다.

또한 메서드들은 이미 선언되었기 때문에 오버라이딩 해주면 되겠죠?

// Marine.java

package cls;

import inter.Unit;

public class Marine implements Unit {

    private int x, y;

    public Marine(int x, int y) {
        this.x = x;
        this.y = y;
    }

    void stimPack() {
        System.out.println("스팀팩 사용");
    }

    @Override
    public void move(int x, int y) {
        System.out.println(x + ", " + y + "로 이동");
    }

    @Override
    public void stop() {
        System.out.println("현위치에서 정지");
    }
}
// Tank.java

package cls;

import inter.Unit;

public class Tank implements Unit {

    private int x, y;

    public Tank(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public void move(int x, int y) {
        System.out.println(x + ", " + y + "로 이동");
    }

    @Override
    public void stop() {
        System.out.println("현위치에서 정지");
    }

    public void changeMode() {
        System.out.println("공격모드 변환");
    }
}
// Dropship.java

package cls;

import inter.Unit;

public class Dropship implements Unit {

    private int x, y;

    public Dropship(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public void move(int x, int y) {
        System.out.println(x + ", " + y + "로 이동");
    }

    @Override
    public void stop() {
        System.out.println("현위치에서 정지");
    }

    public void load() {
        System.out.println("load 함");
    }

    public void unload() {
        System.out.println("unload 함");
    }


}

저는 빈 메서드에 System.out.println()을 넣어서 메인 클래스에서 호출했을 때 메시지를 내주게 하였습니다.


문제 4

문제

다음과 같은 실행결과를 얻도록 코드를 완성하시오.
[Hint] instanceof 연산자를 사용해서 형변환 한다.

[실행결과]
춤을 춥니다.
노래를 합니다.
그림을 그립니다.

메서드명 : action
기능 : 주어진 객체의 메서드를 호출한다.
DanceRobot인 경우, dance()를 호출하고,
SingRobot인 경우, sing()을 호출하고,
DrawRobot인 경우, draw()를 호출한다.
반환타입 : 없음
매개변수 : Robot r - Robot인스턴스 또는 Robot의 자손 인스턴스

다음 클래스가 주어진다

// Main.java

public class Main {
	public static void main(String[] args) {
		Robot[] arr = { new DanceRobot(), new DrawRobot(), new SingRobot() };

		for(int i=0; i<arr.length; i++) {
			action(arr[i]);
		}
        
	}	// main
}
// Robot.java

package cls;

public class Robot { }
// DanceRobot.java

class DanceRobot extends Robot {
	public void dance() {
		System.out.println("춤을 춥니다.");
	}
}
// SingRobot.java

class SingRobot extends Robot {
	public void sing() {
		System.out.println("노래를 합니다.");
	}
}
// DrawRobot.java

class DrawRobot extends Robot {
	public void sing() {
		System.out.println("그림을 그립니다.");
	}
}

해결방법

우선은 배열안에 객체가 생성됩니다. 그리고 반복문 루프를 돌며 action이라는 함수에 arri번째 인덱스의 요소를 넣고 돌려줍니다.
action 메서드는 객체의 메서드를 호출해야하며, 반환타입이 없는 메서드입니다.
매개변수로 Robot의 인스턴스 또는 Robot의 자손 인스턴스를 받습니다.

자 그러면 메인함수 바로 밑에 action 함수를 작성해보겠습니다.

public static void action(Robot robot) {	// 매개변수로 Robot의 인스턴스 받음

}

그렇다면 매개변수로 받을 배열안의 각각의 객체가 우리가 가지고 있는 클래스에서 만들어진것이라면 해당 클래스에 있는 메서드를 출력해주면 되겠죠?

어떤 객체가 비교할 객체를 참조한 것인지 비교할 때 우리는 instanceof를 사용했습니다. 힌트에도 나와있지요?

public static void action(Robot robot) {	// 매개변수로 Robot의 인스턴스 받음
	if(robot instanceof DanceRobot) {
		DanceRobot dr = (DanceRobot) robot;
		dr.dance()
	}
}

이렇게 매개변수로 받는 객체가 DanceRobot 객체를 참조한다면 robotDanceRobot으로 강제 변환하여 dr이라는 변수에 넣어주고 이 dr의 메서드를 실행시켜줍니다.

나머지도 이렇게 해주면 됩니다.

public static void action(Robot robot) {	// 매개변수로 Robot의 인스턴스 받음
	if(robot instanceof DanceRobot) {
		DanceRobot dr = (DanceRobot) robot;
		dr.dance()
	} else if (robot instanceof DrawRobot) {
		((DrawRobot) robot).draw();
	} else if (robot instanceof SingRobot) {
		((SingRobot) robot).sing();
	}
}

이해 되셨나요??

이번 포스팅에서 준비한 내용은 여기까지 입니다.

복습에 많은 도움이 되었기를 바랍니다!

profile
tried ? drinkCoffee : keepGoing;

0개의 댓글