Java 다형성에 대한 자세한 설명(2)

상향 변환 및 하향 변환

상향 변환

정의: 실제로는 하위 클래스 객체를 생성하여 상위 클래스 객체로 사용하는 것입니다 .

문법 형식: 상위 클래스 유형 객체 이름 = 새 하위 클래스 유형()

동물 동물 = new Cat("위안바오", 2);

동물은 상위 클래스 유형이지만 작은 범위에서 큰 범위로의 변환 이므로 하위 클래스 객체를 참조할 수 있습니다 .

특징:

  1. 컴파일 타임 다형성: 상위 클래스 참조 변수는 하위 클래스 객체를 참조할 수 있으며, 컴파일러는 컴파일 타임에 참조 변수의 유형이 객체 유형과 호환되는지 여부를 확인합니다.

  2. 런타임 다형성: 런타임 시 참조 변수가 가리키는 실제 객체 유형에 따라 해당 메서드를 호출하여 메서드의 다형성을 구현합니다.

  3. 메서드 액세스 제한: 상향 변환 후에는 상위 클래스에 선언된 메서드만 호출할 수 있으며 하위 클래스의 새 메서드는 직접 호출할 수 없습니다.

사용 시나리오: 1. 직접 할당 2. 매개변수 전달 메서드 3. 메서드 반환

예 1: 직접 할당:

class Animal {
    void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    void makeSound() {
        System.out.println("Dog barks");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog(); // 直接赋值
        animal.makeSound(); // 调用的是 Dog 类的方法
    }
}

예시 2: 메소드 매개변수 전달

class Shape {
    void draw() {
        System.out.println("draw a shape");
    }
}

class Circle extends Shape {
    @Override
    void draw() {
        System.out.println("draw a circle");
    }
}

class Triangle extends Shape {
    @Override
    void draw() {
        System.out.println("draw a triangle");
    }
}
public class Test {
    public static void shapeDrawing(Shape s) {
        s.draw();
    }

    public static void main(String[] args) {
        Shape s1 = new Circle();
        Shape s2 = new Triangle();

        shapeDrawing(s1);
        shapeDrawing(s2);//方法传参,传递不同子类对象
    }
}

예시 3: 반환 값 만들기

class Vehicle {
    String getType() {
        return "Vehicle";
    }
}

class Car extends Vehicle {
    @Override
    String getType() {
        return "Car";
    }
}

class Bike extends Vehicle {
    @Override
    String getType() {
        return "Bike";
    }
}

public class Test {
    public static Vehicle getType(String type){
        if(type.equals("car")){
            return new Car();
        } else if(type.equals("bike")) {
            return new Bike();
        } else {
            return new Vehicle();
        }
    }

    public static void main(String[] args) {
        Vehicle v1 = getType("car");
        Vehicle v2 = getType("bike");
        Vehicle v3 = getType("plane");

        System.out.println(v1.getType());
        System.out.println(v2.getType());//方法返回,返回的实际上可能是子类对象
        System.out.println(v3.getType());
    }
}

상향 변환의 장점: 코드 구현을 더 간단하고 유연하게 만듭니다.

상향 변환의 단점: 하위 클래스별 메서드를 호출할 수 없습니다.

하향 변환

상향 변환 후 하위 클래스 객체를 상위 클래스 메소드로 사용 하위 클래스의 메소드를 호출할 수 없지만 때로는 하위 클래스의 고유 메소드를 호출해야 하는 경우도 있음 이때: 하위 클래스 객체에 대한 상위 클래스 참조를 복원하기만 하면 , 이는 하락세입니다.

class Animal1 {
    void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog1 extends Animal1 {
    @Override
    void makeSound() {
        System.out.println("Dog barks");
    }

    void fetch() {
        System.out.println("Dog fetch the ball");
    }
}

public class Test1 {
    public static void main(String[] args) {
        Animal1 animal1 = new Dog1();//向上转型

        //向下转型
        if(animal1 instanceof Dog1) {
            Dog1 dog = (Dog1)animal1;//实例类型检查和向下转型
            dog.makeSound();//调用子类的方法
            dog.fetch();//调用子类特有的方法
        }
    }
}

다운캐스팅은 거의 사용되지 않으며 안전하지 않습니다. 변환에 실패하면 런타임에 예외가 발생합니다. Java에서는 다운캐스팅의 안전성을 높이기 위해 objectof( 객체가 특정 클래스의 인스턴스인지 아니면 하위 클래스의 인스턴스인지 확인하는 데 사용됨 )가 도입되었으며, 표현식의 결과가 true인 경우 안전하게 변환할 수 있습니다.

다형성의 장점과 단점

이점

다음 코드가 있다고 가정합니다.

class Shape {
    //属性。。。
    public void draw() {
        System.out.println("画图形");
    }
}

class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("⎕");
    }
}

class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("◉");
    }
}

class Star extends Shape {
    @Override
    public void draw() {
        System.out.println("★");
    }
}

1. 코드의 "순환 복잡성"을 줄이고 많은 수의 if-else 사용을 피할 수 있습니다.

순환 복잡도(Cyclomatic Complexity): 코드 조각이 얼마나 복잡한지 설명하는 방법입니다. 코드가 간단하면 상대적으로 간단하고 이해하기 쉽지만, 조건 분기나 루프 문이 많으면 이해하기 더 복잡하다고 간주됩니다.

따라서 한 코드에 포함된 조건문과 루프 문의 개수를 간단하고 대략적으로 계산할 수 있는데, 이 수치를 "순환 복잡도"라고 합니다. 메소드의 순환 복잡도가 너무 높으면 리팩토링을 고려해야 합니다.

회사마다 순환 복잡도에 대한 사양이 다르지만 일반적으로 10을 초과하지 않습니다.

예를 들어 지금 우리에게 필요한 것은 하나의 도형이 아닌 여러 도형을 출력하는 것인데, 다형성을 기반으로 하지 않는 경우 구현 코드는 다음과 같습니다.

public static void drawShapes() {
    Rect rect = new Rect();
    Circle circle = new Circle();
    Star star = new Star();
    String[] shapes = {"circle", "rect", "circle", "rect", "star"};
    for(String shape : shapes) {
        if(shape.equals("circle")){
            circle.draw();
        } else if(shape.equals("rect")) {
            rect.draw();
        } else if(shape.equals("star")) {
            star.draw()
        }
    }

}

다형성을 사용하면 if-else 분기 문을 너무 많이 작성할 필요가 없으며 코드가 더 간단해집니다.

Shape[] shapes = {new Circle(), new Rect(), new Circle(), new Rect(), new Star()};
for(Shape shape : shapes) {
    shape.draw();
}

2. 확장성 강화

새로운 도형을 추가하고 싶을 경우 다형성을 이용하면 코드 수정 비용이 상대적으로 저렴합니다.

class Triangle extends Shape {
    @Override 
    public void draw() {
        System.out.println("▲");
    }
}

클래스의 호출자(주 함수)에 대해서는 새 클래스의 인스턴스를 생성하기만 하면 수정 비용이 매우 낮습니다.

다형성을 사용하지 않는 상황에서는 drawShapes의 if-else를 어느 정도 수정해야 하며 수정 비용이 더 높습니다.

결점

코드 실행 효율성 감소

1. 속성에는 다형성이 없습니다. 상위 클래스와 하위 클래스 모두 동일한 이름의 속성을 갖는 경우 상위 클래스의 참조를 통해 상위 클래스의 멤버 속성만 참조할 수 있습니다.

2. 생성자에는 다형성이 없습니다.

 

추천

출처blog.csdn.net/asdssadddd/article/details/132400725