본문 바로가기

Java

Java- Object 클래스

Object 클래스

  최상위 클래스로서 모든 클래스들은 Object 클래스의 상속한다.

 equals 메소드

  참조값의 비교를 할 때에는 "==" 비교 연산자를 사용하고, 값의 비교를 할 때에는 equals() 메소드를 사용한다.

class INum{
    int num;
    INum(int num){
        this.num = num;
    }

    @Override  //Object 클래스 오버라이딩
    public boolean equals(Object obj){  //매개변수의 타입이 Object이므로 어떠한 인스턴스든 참조가 가능하다.
        //equals 메소드는 단순히 값만 같은지 비교함
        return this.num == ((INum) obj).num;
    }
    void isEquals(Object obj){
        if(this.equals(obj))
            System.out.println("두 인스턴스 내용 동일");
        else
            System.out.println("두 인스턴스 내용 다름");
    }
}

public class EqualsMethod {
    public static void main(String[] args) {
        INum inum1 = new INum(10);
        INum inum2 = new INum(15);
        INum inum3 = new INum(10);

       inum1.isEquals(inum2);  //값 비교
       inum1.isEquals(inum3);  //값 비교

       isSameInstance(inum1,inum2);  //참조 비교
       isSameInstance(inum1,inum3);  //참조 비교
    }
    public static void isSameInstance(Object obj1, Object obj2){
        if(obj1 == obj2)
            System.out.println("두 인스턴스는 동일 인스턴스");
        else
            System.out.println("두 인스턴스는 다른 인스턴스");
    }
}

두 인스턴스 내용 다름
두 인스턴스 내용 동일
두 인스턴스는 다른 인스턴스
두 인스턴스는 다른 인스턴스

 inum1,2,3는 서로 다른 인스턴스들이지만 inum1,3는 그 내부값은 동일하다. 이를 비교할 수 있는 equals 메소드를 오버라이딩하였다.

  clone 메소드

  clone() 메소드는 해당 메소드가 속해있는 인스턴스를 복사하여 생성한 후 생성한 인스턴스를 반환한다. 이는 Object 클래스에 구현되어 있다.

 얕은 복사가 일어나는 clone 메소드

class Point implements Cloneable{
    private int xpos;
    private int ypos;
    public Point(int xpos, int ypos){
        this.xpos = xpos;
        this.ypos = ypos;
    }

    @Override
    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
    void changePos(int xpos, int ypos){
        this.xpos = xpos;
        this.ypos = ypos;
    }
    void showInfo(){
        System.out.printf("(%d, %d)\n",xpos,ypos);
    }
}

class Rectangle implements Cloneable{
    Point upperLeft;
    private Point lowerRight;
    public Rectangle(int x1, int y1, int x2, int y2){
        upperLeft = new Point(x1,y1);
        lowerRight = new Point(x2,y2);
    }
    void changePos(int x1, int y1, int x2, int y2){
        upperLeft.changePos(x1,x2);
        lowerRight.changePos(x2,y2);
    }
    @Override
    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
    void showInfo(){
        System.out.print("upperLeft"); upperLeft.showInfo();
        System.out.print("lowerRight"); lowerRight.showInfo();
    }
}

public class ShallowCopy {
    public static void main(String[] args) {
        try{
            Rectangle r1 = new Rectangle(1,1,2,2);
            Rectangle r2 = (Rectangle)r1.clone();  //멤버간 얕은 복사(멤버간 참조값이 동일함)

            r1.showInfo();
            r2.showInfo();
            System.out.println();

            r1.changePos(5,5,6,6);
            r1.showInfo();
            r2.showInfo();

            if(r1.upperLeft == r2.upperLeft)  //얕은 복사 발생
                System.out.println("same");  //r1과 r2는 다르지만 멤버 변수는 동일 인스턴스를 참조함
        }
        catch(CloneNotSupportedException e){
            e.getStackTrace();
        }
    }
}


//    실행결과

//    upperLeft(1, 1)
//    lowerRight(2, 2)
//    upperLeft(1, 1)
//    lowerRight(2, 2)
//
//    upperLeft(5, 6)
//    lowerRight(6, 6)
//    upperLeft(5, 6)
//    lowerRight(6, 6)

 얕은 복사란 참조값의 복사를 의미한다. 즉, 같은 인스턴스를 공유한다는 것이다. 이 경우 동일한 참조값을 공유하는 한 참조변수의 값의 변화가 다른 참조변수에도 영향을 끼치게 된다. 

 

 위 예제에서는 Object 클래스의 메소드 protected Object clone() throws CloneNotSupportedException 메소드를 오버라이딩 하여 구현했다. 참고로 오버라이딩 과정에서 protected가 public으로 변환되었는데 제한적인 범위에서 개방적인 범위로서의 변환은 오버라이딩시 허용된다

 

 clone() 메소드의 오버라이딩 과정에서의 반환 형 변환

위 코드의  47번째 줄을 보면 "(Rectangle)r1.clone();" 이 보일 것이다. 여기서 Rectangle로 형 변환을 해주는 이유는 clone()의 반환형이 Object이기 때문이다. 따라서 clone() 메소드를 호출한 후 반환되는 값은 항상 형 변환을 진행해주어야 하는데 이를 오버라이딩 과정에서 좀더 간편하게 진행할 수 있다.

class Point{
    //...중략
}

class Rectangle{
    //...중략
    @Override
    public Rectangle clone() throws CloneNotSupportedException{  //반환형이 Object -> Rectangle
        return (Rectangle)(super.clone());  //메소드 내에서 형 변환
    }
}

public class ShallowCopy {
    public static void main(String[] args) {
        try{
            Rectangle r1 = new Rectangle(1,1,2,2);
            Rectangle r2 = r1.clone();  //형 변환 과정이 간편해짐
				//...중략
        }
        catch(CloneNotSupportedException e){
            e.getStackTrace();
        }
    }
}

 이를 보면 원래는 protected Object clone() throws CloneNotSupportedException{ }이

public Rectangle clone() throws CloneNotSupportedException{ }으로 오버라이딩 되었음을 확인할 수있다.

 

  다시말해 오버라이딩 과정에서 접근 지시 제어자와 반환형이 바뀌는 것이 가능하다.

 

   오버라이딩시...

  • 접근 지시 제어자 변경가능 (단 제한적인 범위에서 개방적인 범위로)
  • 반환형 변경 가능 (단 해당 메소드의 클래스 이름과 같도록만 가능함)

 

  깊은 복사가 일어나는 clone 메소드

class Point implements Cloneable{
    private int xpos;
    private int ypos;
    public Point(int xpos, int ypos){
        this.xpos = xpos;
        this.ypos = ypos;
    }

    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
    void changePos(int xpos, int ypos){
        this.xpos = xpos;
        this.ypos = ypos;
    }
    void showInfo(){
        System.out.printf("(%d, %d)\n",xpos,ypos);
    }
}

class Rectangle implements Cloneable{
    Point upperLeft;
    private Point lowerRight;
    public Rectangle(int x1, int y1, int x2, int y2){
        upperLeft = new Point(x1,y1);
        lowerRight = new Point(x2,y2);
    }
    void changePos(int x1, int y1, int x2, int y2){
        upperLeft.changePos(x1,x2);
        lowerRight.changePos(x2,y2);
    }
    @Override
    public Object clone() throws CloneNotSupportedException{ //깊은 복사가 발생하도록 오버라이딩
        Rectangle copy = (Rectangle)super.clone();
        //깊은 복사
        copy.upperLeft = (Point)upperLeft.clone();  //멤버들도 새로이 생성
        copy.lowerRight = (Point)lowerRight.clone();  //멤버들도 새로이 생성

        return copy;  //깊은 복사된 Rectangle copy가 Object 형으로 반환
    }
    void showInfo(){
        System.out.print("upperLeft"); upperLeft.showInfo();
        System.out.print("lowerRight"); lowerRight.showInfo();
    }
}

public class DeepCopy {
    public static void main(String[] args) {
        try{
            Rectangle r1 = new Rectangle(1,1,2,2);
            Rectangle r2 = (Rectangle)r1.clone();

            r1.showInfo();
            r2.showInfo();
            System.out.println();

            r1.changePos(5,5,6,6);
            r1.showInfo();
            r2.showInfo();

            if(r1.upperLeft == r2.upperLeft)
                System.out.println("same");
            else
                System.out.println("different");
        }
        catch(CloneNotSupportedException e){
            e.getStackTrace();
        }
    }
}

//    실행결과

//    upperLeft(1, 1)
//    lowerRight(2, 2)
//    upperLeft(1, 1)
//    lowerRight(2, 2)
//
//    upperLeft(5, 6)
//    lowerRight(6, 6)
//    upperLeft(1, 1)
//    lowerRight(2, 2)
//    different

  전반적인 코드는 얕은 복사의 코드와 동일하되 Rectangle의 clone( ) 메소드 오버라이딩 부분만 좀 바뀌었다. 기본적으로 clone( )은 해당 메소드의 인스턴스와 멤버가 같은 인스턴스를  "생성(new)"하여 반환하기에 만일 내부 변수에 인스턴스가 없는 경우 깊은 복사와 같은 효과를 낼 수 있다. 그러나 멤버에 인스턴스가 존재 시 해당 멤버들은 얕은 복사가 되기 때문에 이를 해결해 줄 필요가 있을 수도 있다(깊은 복사가 필요한 상황이 있는 경우). 

  위 경우 Rectangle은 Point라는 인스턴스를 멤버로 갖는다. 따라서 그냥 clone()을 쓰면 upperLeft와 lowerRight에 얕은 복사가 진행된다. 그래서 Point 인스턴스 각각에도 clone()을 해주어 아예 Point 인스턴스를 new 해줌으로서 문제를 해결했다(새로운 인스턴스를 생성함으로써 인스턴스가 구분됨). 

'Java' 카테고리의 다른 글

Java- BigInteger, BigDecimal 클래스  (0) 2023.01.03
Java- 래퍼 클래스  (0) 2023.01.03
Java- 예외처리  (0) 2023.01.02
Java- 인터페이스와 추상클래스 비교  (0) 2023.01.01
Java- interface  (0) 2022.12.30