110. 다형성
package day09;//다형성
class A{ int x;} //x
class B extends A{int y;} //x,y
public class Test110 {
public static void main(String[] args) {
// 기존에 객체 생성하던 방식
A a = new A();
B b = new B();
//다형성: 부모 타입의 변수에 자식 객체를 넣는다.
A a1= new B();
System.out.println(a1.x);
//부모 객체가 자식 변수에 들어가는건 안된다.
//B b1 = new A();
}
}
부모 타입의 변수에 자식의 객체를 넣어 생성하였다. 부모는 자식 클래스 안에 뭐가 들어있는지 모르기 때문에 자식 클래스에 있는 변수를 사용할 수는 없다.
111.
package day09;
class AClass{ //x,func
int x;
void func() {
System.out.println("AAA");
}
}
class BClass extends AClass{ //x,y,func,sub
int y ;
@Override
void func() {
System.out.println("BBB");
}
void sub() {
System.out.println("subsub");
}
}
public class Test111 {
public static void main(String[] args) {
AClass a = new BClass();
// AClass a = new AClass();
BClass b = null;
//참조변수가 가르키는 실제객체가 BClass의 객체가 맞는지 판단하고
if(a instanceof BClass) { //맞으면 true
System.out.println("형변환 가능");
b = (BClass)a; //실행해라
}else {
System.out.println("형변환 불가능");
}
AClass a1 = null;
BClass b1 = new BClass();
BClass b2 = null;
a1= (AClass)b1; //자식 객체를 형 변환하여 부모 참조 변수에 넣었다.
//형 변환 생략해도 에러x 자식을 부모로 업캐스팅.
b2= (BClass)a1; // 위에서 a1에 BClass를 넣었음. 한번 형 변환해서 넣어주면 가능.
// 실체가 같은 타입이더라도 들고 있는 변수가 부모라면, 맞는 타입에 대입할 때도
// 형 변환이 반드시 필요함. 부모를 자식으로 형 변환하는걸 다운캐스팅
AClass a2 = new AClass();
BClass b3 = null;
b3 =(BClass)a2; //AClass (부모)를 BClass (자식)으로 형변환 할 수 없다!
//BClass b3 = new AClass(); 와 동일.
b3.func();
//////////////
AClass a4 = null;
BClass b4 = new BClass();
a4 = (AClass)b4; //a는 AClass타입. 밑에 코드와 동일함. B로 객체를 만들어도 접근 못함.
a4.func();
///////////
AClass a5 = new AClass(); //x, func("AAA")
AClass a6 = new BClass(); //x, @func("BBB"), (y, sub()접근못함)
System.out.println(a.x);
a.func();
System.out.println(a1.x);
a1.func();
}
}
Day5 노트 참고:
*참조 변수 (래퍼런스 변수): 객체를 가리키는, 객체의 주소 (래퍼런스)를 담고 있을 변수 -> 객체 생성 전, 참조 변수 먼저 선언만 하면 메모리에 아직 객체가 생성되어 올라간 것이 아니다. 그냥 주소 담을 수 있는 변수만 만들어 놓은 것.
112. 다형성
package day09; //다형성
class Person{}
class Student extends Person{}
class Teacher extends Person{}
class Professor extends Teacher{}
public class Test112 {
static void print(Person p) { //부모 변수의 자식 객체 받아줄 수 있다.
// p = new Student(); = Teacher(); = Student(); = Professor();
if(p instanceof Person) {System.out.println("Person!");}
if(p instanceof Student) {System.out.println("Student!");}
if(p instanceof Teacher) {System.out.println("Teacher!");}
if(p instanceof Professor) {System.out.println("Professor!");}
}
public static void main(String[] args) {
// print(new Person());
print(new Student());
// print(new Teacher());
// print(new Professor());
}
}
먼저 Person 클래스를 선언하고 세 개의 자식 클래스를 만들어 준다.
Test112 클래스에 있는 프린트 메서드는 매개변수가 있는데 만약 모든 클래스를 경우에 따라 다르게 출력하고 싶다면 매개변수로 부모 타입을 지정해주면 된다. 부모 변수의 자식 객체를 받아줄 수 있다.
그런데! 만약에 프로페서로 객체를 생성해서 인자로 주면 Person, Teacher, Professor가 전부 출력된다. 프로페서는 티쳐를 상속하고, 티쳐는 퍼슨을 상속한다. 스튜던트를 상속받지 않았기에 instanceof에 false가 되어 출력되지 않고 나머지 세 개만 출력이 된다.
package selfstudy;
class Person{}
class Student extends Person{}
class Teacher{}
class Professor extends Teacher{}
public class self1 {
static void print(Person p) {
if(p instanceof Person) {System.out.println("Person");}
if(p instanceof Student) {System.out.println("Student");}
}
static void print(Teacher t) {
if(t instanceof Teacher) {System.out.println("Teacher");}
if(t instanceof Professor) {System.out.println("Professor");}
}
public static void main(String[] args) {
print(new Professor());
}
}
코드를 살짝 바꿔 보았다. 이렇게 하면 티쳐를 상속한 Professor는 메서드 오버로딩된 print(Teacher t)를 실행시켜 Teacher와 Professor를 출력한다.
113.
package day09;
import java.util.Scanner;
class AA{
int x;
}
class BB extends AA{
int y;
}
public class Test113 {
public static void main(String[] args) {
AA a = new AA();
AA a1 = new BB();
Object o = new AA(); //o는 자식객체가 뭘 가지고 있는지 모른다..
Object o2 = new BB();
Object o3 = new Scanner(System.in);
if(o instanceof AA) {
AA abc = (AA)o;
}
}
}
Object는 완전 조상 클래스.
그래서 부모 클래스인 o는 자식 클래스인 AA로 객체 생성이 가능하다.
114. 매개변수의 다형성
package day09; //매개변수의 다형성
class Product{
int price; //제품 가격
int bonusPoint; //제품 구매 시 제공하는 포인트 점수
Product(int price){
this.price = price;
bonusPoint = (int)(price/10); //제품 가격의 10%
}
}
class LGTv extends Product{
LGTv(){
super(100);
}
@Override //투스트링 오버라이딩
public String toString() {
return "엘지 티비";
}
}
class SComputer extends Product{
SComputer(){
super(200);
}
@Override //투스트링 오버라이딩
public String toString() {
return "삼성 컴퓨터";
}
}
class Buyer{ //고객
int money = 1000; //지갑
int bonusPoint = 0; //고객 포인트
void buy(Product p) { //매개변수 다형성. 무슨 타입을 몇개 써야 잘 작동하는지 생각하기.
if(money < p.price) {
System.out.println("돈이 모자라네요. 다음에 다시 오세요~");
return;
}
money -= p.price;
bonusPoint += p.bonusPoint;
System.out.println(p+ "을/를 구입하셨습니다.");
}
}
public class Test114 {
public static void main(String[] args) {
Buyer b= new Buyer();
LGTv tv = new LGTv();
SComputer com = new SComputer();
b.buy(tv);
b.buy(com);
System.out.println("현재 남은 돈은 " + b.money + "원 입니다.");
System.out.println("현재 포인트는 "+ b.bonusPoint + "점 입니다.");
}
}
먼저 봐야할 것!
프로덕트 클래스엔 기본 생성자가 없는 점. 만약 프로덕트 클래스를 상속받으면, 프로덕트의 매개 변수를 가진 생성자에 값을 던져줄 필요가 있다. 이 경우엔 LGTv의 기본 생성자 안에 super(100)으로 하여 프로덕트 생성자에 매개변수 price로 값 100을 준다.
투 스트링! 원래는 주소 값을 주는 메서드. 여기서는 해당 클래스를 호출하면 리턴해주는 값으로 문구를 오버 라이딩해줬다. 21일에 자세히 배운다고 하니 참고하자.
115.
package day09; //오버라이딩으로 다형성 실현
class Shape{
public void draw() {
System.out.println("Shape");
}
}
class Line extends Shape{
@Override
public void draw() {
System.out.println("Line");
}
}
class Rect extends Shape{
@Override
public void draw() {
System.out.println("Rect");
}
}
class Circle extends Shape{
@Override
public void draw() {
System.out.println("Circle");
}
}
public class Test115 {
static void paint(Shape s) { //매개변수의 다형성
s.draw(); //s에 들어있는거에 따라서 line, shape, rect, circle 전부 될 수 있음.
}
public static void main(String[] args) {
Line line = new Line();
paint(line);
paint(new Shape());
paint(new Rect());
paint(new Circle());
}
}
116.
package day09; //매개변수 다형성 응용. 자료구조 linked list 이용.
//링크드 리스트 구현
class ShapeClass{
ShapeClass next;
ShapeClass() {next =null;}
void draw() {
System.out.println("shape");
}
}
class LineClass extends ShapeClass{
@Override
void draw() {
System.out.println("Line");
}
}
class RectClass extends ShapeClass{
@Override
public void draw() {
System.out.println("Rect");
}
}
class CircleClass extends ShapeClass{
@Override
public void draw() {
System.out.println("Circle");
}
}
public class Test116 {
public static void main(String[] args) {
ShapeClass start,last, tmp; //last는 마지막 링크가 뭐였는지 tmp는 객체 생성할때 잠깐 쓰임(임시변수)
start = new LineClass();
last = start;
tmp = new RectClass();
last.next= tmp;
last=tmp;
tmp= new LineClass();
last.next=tmp;
last = tmp;
tmp=new CircleClass();
last.next=tmp;
last = tmp;
//모든 도형 출력
ShapeClass s = start;
while(s != null) {
s.draw();
s = s.next;
}
}
}
117.
package day09; //추상 클래스
abstract class Super{
abstract void func();
abstract void func1();
//일반 변수들과 메서드들도 작성 가능. 하지만 객체 생성 불가능하여 상속 받아 사용해야함.
int a = 10;
void aaa() {
}
}
class Sub extends Super{
@Override
void func() { // 부모 클래스에 있는 모든 추상 메서드를 상속 받아 작성해야함.
}
@Override
void func1() { //이건 안쓸거면 그냥 비워두기
}
}
public class Test117 {
public static void main(String[] args) {
//미완성 형태의 추상 클래스로는 객체 생성 불가!!
//Super s = new Super();
Sub sub = new Sub();
sub.func();
}
}
118.
package day09; //추상 클래스 사용
abstract class Calculator{
abstract int add(int a, int b); //두 정수의 합을 구하여 리턴
abstract int substract(int a, int b); //두 정수의 차를 구하여 리턴
abstract double average(int []a); //배열에 저장된 정수의 평균값 리턴
}
class MyCalc extends Calculator{
@Override
int add(int a, int b) { //리턴 되는 값이 있을 때는 출력문 안에서!
return a + b;
}
@Override
int substract(int a, int b) {
return a-b;
}
@Override
double average(int[] a) {
double sum =0;
for(int i : a) { //하나씩 올라가는 i가 아님. 그냥 값을 가져오는 for-each문
sum += i;//난 a[i]으로 했는데 안 돌아감.
}
return sum / a.length;
}
}
public class Test118 {
public static void main(String[] args) {
MyCalc c = new MyCalc();
System.out.println(c.add(10,5));
System.out.println(c.substract(10,5));
System.out.println(c.average(new int[] {1,2,3,4,5}));
//int [] arr = {1,2,3,4,5}; 위 배열은 이 배열의 간결한 버전.
}
}
119.
package day09;
interface Test{
public static final int NUM =1; //상수
public abstract void add(); //추상메서드
int x =100;//상수
int func();//추상메서드
}
interface Test2{
void func22();//추상메서드
}
class AA1{
int x =10;
}
class Inter extends AA1 implements Test , Test2{
//이렇게 상속도 받고 인터페이스도 구현할 수 있음
@Override
public void add() {
System.out.println("imple~");
}
@Override
public int func() {
return 0;
}
@Override
public void func22() {
System.out.println("func2");
}
}
public class Test119 {
public static void main(String[] args) {
Inter i = new Inter();
i.add();
i.func22();
//다형성
//인터페이스 타입의 변수에, 구현 클래스로 생성한 객체 담을 수 있다.(부모 역할)
Test t = new Inter(); //테스트 타입을 구현시켜놓은 클래스.
Test2 tt =new Inter(); //테스트를 구현해서 만들어놓은 클래스는 모두 테스트의 자식.
}
}
만약에! 여기서 i.x 와 t.x를 호출하게 되면 t.x 는 제대로 출력 되지만 i.x는 The field is ambiguous 에러가 뜨게 된다. 왜냐, 테스트를 구현해놓은 클래스 t 안에는 x가 값으로 100을 가진 그 x 하나밖에 없지만 테스트 인터페이스와 AA1 클래스를 상속받은 i 안에는 변수 x가 두 개가 있기 때문에..