IT story

추상 클래스를 인스턴스화 할 수 있습니까?

hot-time 2020. 2. 9. 19:26
반응형

추상 클래스를 인스턴스화 할 수 있습니까?


인터뷰 중 하나에서 "추상 클래스를 인스턴스화 할 수 있는지 여부"라는 질문을 받았습니다.

답장은 "아니요."라고 대답했습니다. 그러나 면접관은 "잘못, 우리는 할 수있다"고 말했다.

나는 이것에 대해 조금 논쟁했다. 그런 다음 집에서 직접 시도해 보라고 말했습니다.

abstract class my {
    public void mymethod() {
        System.out.print("Abstract");
    }
}

class poly {
    public static void main(String a[]) {
        my m = new my() {};
        m.mymethod();
    }
}

여기에서는 클래스의 인스턴스를 만들고 추상 클래스의 메서드를 호출합니다. 누구든지 나에게 이것을 설명해 주시겠습니까? 인터뷰 중에 정말 잘못 되었습니까?


여기, 내 수업의 인스턴스를 만들고 있습니다

아니요, 여기서 추상 클래스의 인스턴스를 만들지 않습니다. 오히려 추상 클래스 익명 서브 클래스 인스턴스를 작성하는 것 입니다. 그런 다음 추상 클래스 참조에서 하위 클래스 객체를 가리키는 메소드를 호출 합니다 .

이 동작은 JLS 섹션 15.9.1 에 명확하게 나와 있습니다 .

클래스 인스턴스 작성 표현식이 클래스 본문으로 끝나면 인스턴스화되는 클래스는 익명 클래스입니다. 그때:

  • T가 클래스를 나타내는 경우 T로 명명 ​​된 클래스의 익명 직접 서브 클래스가 선언됩니다. T로 표시된 클래스가 최종 클래스 인 경우 컴파일 타임 오류입니다.
  • T가 인터페이스를 나타내는 경우 T로 명명 ​​된 인터페이스를 구현하는 Object의 익명 직접 서브 클래스가 선언됩니다.
  • 두 경우 모두 서브 클래스의 본문은 클래스 인스턴스 작성 표현식에 제공된 ClassBody입니다.
  • 인스턴스화되는 클래스는 익명 서브 클래스입니다.

강조합니다.

또한 JLS-섹션 12.5 에서 객체 생성 프로세스 에 대해 읽을 수 있습니다 . 여기에서 하나의 진술을 인용하겠습니다 :-

새 클래스 인스턴스가 작성 될 때마다 클래스 유형에 선언 된 모든 인스턴스 변수와 클래스 유형의 각 수퍼 클래스에 선언 된 모든 인스턴스 변수 (숨겨 질 수있는 모든 인스턴스 변수 포함)를위한 공간이있는 메모리 공간이 할당됩니다.

새로 작성된 오브젝트에 대한 참조가 결과로 리턴되기 직전에 표시된 생성자가 다음 프로 시저를 사용하여 새 오브젝트를 초기화하도록 처리됩니다.

내가 제공 한 링크에서 전체 절차에 대해 읽을 수 있습니다.


실제로 인스턴스화되는 클래스가 Anonymous SubClass 임을 확인하려면 두 클래스를 모두 컴파일하면됩니다. 해당 클래스를 두 개의 다른 파일에 넣었다고 가정하십시오.

My.java :

abstract class My {
    public void myMethod() {
        System.out.print("Abstract");
    }
}

Poly.java :

class Poly extends My {
    public static void main(String a[]) {
        My m = new My() {};
        m.myMethod();
    }
}

이제 두 소스 파일을 모두 컴파일하십시오.

javac My.java Poly.java

이제 소스 코드를 컴파일 한 디렉토리에 다음 클래스 파일이 표시됩니다.

My.class
Poly$1.class  // Class file corresponding to anonymous subclass
Poly.class

해당 클래스를 참조하십시오 Poly$1.class. 아래 코드를 사용하여 인스턴스화 한 익명 서브 클래스에 해당하는 컴파일러가 생성 한 클래스 파일입니다.

new My() {};

따라서 다른 클래스가 인스턴스화되고 있음이 분명합니다. 단지 그 클래스에는 컴파일러가 컴파일 한 후에 만 ​​이름이 지정됩니다.

일반적으로 클래스의 모든 익명 서브 클래스는 다음과 같은 방식으로 이름이 지정됩니다.

Poly$1.class, Poly$2.class, Poly$3.class, ... so on

이 숫자는 해당 익명 클래스가 묶는 클래스에 나타나는 순서를 나타냅니다.


위의 클래스는 my추상 클래스 의 서브 클래스 인 익명의 내부 클래스를 인스턴스화합니다 . 추상 클래스 자체를 인스턴스화하는 것과 완전히 동일하지는 않습니다. OTOH, 모든 서브 클래스 인스턴스는 모든 수퍼 클래스 및 인터페이스의 인스턴스이므로 대부분의 추상 클래스는 실제 서브 클래스 중 하나를 인스턴스화하여 실제로 인스턴스화됩니다.

면접관이 방금 "잘못!" 설명하지 않고이 예제를 고유 한 반례로 제시했지만, 그는 자신이 무엇을 말하는지 모른다고 생각합니다.


= my() {};은 객체의 간단한 인스턴스화가 아닌 익명의 구현이 있음을 의미합니다 = my(). 추상 클래스를 인스턴스화 할 수 없습니다.


당신이 할 수있는 관찰 :

  1. poly연장 my되나요? 이건 쓸모 없다 ...
  2. 편집 결과는 무엇입니까? 세 개의 파일 : my.class, poly.classpoly$1.class
  3. 우리가 그런 추상 클래스를 인스턴스화 할 수 있다면 인터페이스도 인스턴스화 할 수 있습니다 ... 이상한 ...


추상 클래스를 인스턴스화 할 수 있습니까?

아니요, 우리는 할 수 없습니다. 우리가 할 수있는 일은 익명 클래스 (세번째 파일)를 만들고 인스턴스화하는 것입니다.


슈퍼 클래스 인스턴스화는 어떻습니까?

추상 슈퍼 클래스는 우리 가 아닌 자바에 의해 인스턴스화됩니다 .

편집 : 그를 테스트하도록 요청

public static final void main(final String[] args) {
    final my m1 = new my() {
    };
    final my m2 = new my() {
    };
    System.out.println(m1 == m2);

    System.out.println(m1.getClass().toString());
    System.out.println(m2.getClass().toString());

}

출력은 다음과 같습니다

false
class my$1
class my$2

한 줄로 간단히 대답 할 수 있습니다.

아니요 , 추상 클래스를 인스턴스화 할 수 없습니다.

그러나 면접관은 여전히 ​​동의하지 않습니다. 그러면 말할 수 있습니다.

익명 클래스를 만들 수 있습니다.

그리고 익명 클래스에 따르면 클래스 는 동일한 장소 / 라인에서 선언되고 인스턴스화됩니다.

따라서 면접관은 자신의 신뢰 수준과 OOP에 대해 얼마나 많이 알고 있는지 관심이있을 수 있습니다.


기술 부분은 다른 답변에서 잘 다루어졌으며 주로
"그는 잘못 되었습니다. 그는 물건을 알지 못하고 그에게 가입하여 모든 것을 지우십시오 :)"

나는 이것이 다른 질문으로 언급 될 수 있다는 사실을 다루고 싶다. 이것은 많은 질문자 들이 당신에 대해 더 많이 알기위한 중요한 도구이며, 어려운 상황과 비정상적인 상황에 어떻게 대응 하는가에 대한 것이다. 당신에게 잘못된 코드를 제공함으로써, 아마도 당신이 말다툼했는지를 알고 싶었을 것입니다 . 이와 비슷한 상황에서 선배들과 대항 할 자신이 있는지를 알기 위해.

추신 : 이유를 모르겠지만 면접관이이 게시물을 읽은 느낌이 있습니다.


추상 클래스는 인스턴스화 할 수 없지만 서브 클래스화할 수 있습니다. 이 링크를보십시오

가장 좋은 예는

하지만 캘린더 클래스) (추상 메소드의 getInstance를 가지고 ,하지만 당신은 때를 말한다Calendar calc=Calendar.getInstance();

calc는 "GregorianCalendar extends Calendar "로 GregorianCalendar 클래스의 클래스 인스턴스를 참조합니다.

사실 익명의 내부 유형을 사용하면 추상 클래스의 이름없는 서브 클래스 와이 인스턴스 를 만들 수 있습니다 .


기술 답변

추상 클래스는 인스턴스화 할 수 없습니다. 이것은 정의와 디자인에 의한 것입니다.

JLS 8 장에서 제공합니다.

명명 된 클래스는 abstract (§8.1.1.1)로 선언 될 수 있으며 불완전하게 구현 된 경우 abstract로 선언해야합니다. 이러한 클래스는 인스턴스화 할 수 없지만 서브 클래스로 확장 할 수 있습니다.

Classes.newInstance ()에 대한 JSE 6 java doc에서 :

InstantiationException-이 Class가 추상 클래스, 인터페이스, 배열 클래스, 프리미티브 타입, 또는 void를 나타내는 경우 또는 클래스에 null 생성자가없는 경우 또는 다른 이유로 인스턴스화에 실패한 경우.

물론 추상 클래스 (익명 서브 클래스 포함)의 구체적 서브 클래스를 인스턴스화 할 수 있으며 추상 타입에 대한 객체 참조의 타입 캐스트를 수행 할 수도 있습니다.

이것에 대한 다른 각도-팀 플레이 및 소셜 인텔리전스 :

이러한 기술 오해는 복잡한 기술과 법적 사양을 다룰 때 현실 세계에서 자주 발생합니다.

여기서 "사람 기술"이 "기술 기술"보다 더 중요 할 수 있습니다. 경쟁적으로 공격적으로 논란의 여지를 밝히려고 노력한다면 이론적으로 옳을 수는 있지만 싸울 수 / "손상"/ 가치보다 적을 만드는 데 더 많은 피해를 줄 수 있습니다. 차이를 해결할 때 화해하고 이해하십시오. 누가 알겠는가-아마도 당신은 "올바른"것이지만 용어에 대해 약간 다른 의미를 가지고 일하고 있습니까 ??

누가 알겠는가-인터뷰 담당자는 고의로 약간의 갈등 / 오해를 일으켜 어려운 상황에 처하게하고 감정적, 사회적으로 어떻게 행동하는지 볼 수 있습니다. 동료들과 우아하고 건설적이고, 노인들의 조언을 따르고, 이메일이나 전화를 통해 문제 / 오해를 해결하기 위해 인터뷰 후에 진행하십시오. 당신이 동기를 부여하고 세부적인 것을 보여줍니다.


모든 사람이 대답했을 때 인스턴스화 abstract class할 수 없는 잘 확립 된 사실입니다 .

프로그램이 익명 클래스를 정의 할 때, 컴파일러는 실제로 다른 이름으로 새 클래스 (패턴이 만들어 익명 클래스 번호입니다)EnclosedClassName$nn

따라서이 Java 클래스를 디 컴파일하면 다음과 같은 코드를 찾을 수 있습니다.

내 수업

abstract class my { 
    public void mymethod() 
    { 
        System.out.print("Abstract"); 
    }
} 

poly $ 1.class ( "익명 클래스"의 생성 된 클래스)

class poly$1 extends my 
{
} 

ploly.cass

public class poly extends my
{
    public static void main(String[] a)
    {
        my m = new poly.1(); // instance of poly.1 class NOT the abstract my class

        m.mymethod();
    }
}

아니, 당신은 추상 클래스를 인스턴스화 할 수 없습니다. 우리는 익명 클래스 만 인스턴스화합니다. 추상 클래스에서 우리는 추상 메소드를 선언하고 구체적인 메소드 만 정의합니다.


추상 클래스

  • 추상 클래스의 객체를 만들 수 없습니다
  • 변수를 만들 수 있습니다 (데이터 유형처럼 동작 할 수 있음)
  • 자식이 부모의 추상 메서드를 하나 이상 재정의 할 수 없으면 자식도 추상이됩니다.
  • 추상 클래스는 자식 클래스 없이는 쓸모가 없습니다

추상 클래스의 목적은 기본처럼 행동하는 것입니다. 상속 계층 구조에서는 추상 클래스가 맨 위에 나타납니다.


당신은 말할 수 있습니다 :
우리는 추상 클래스를 인스턴스화 할 수는 없지만 new키워드를 사용 {}하여 추상 클래스의 끝에 구현 본문으로 추가하여 익명 클래스 인스턴스를 만들 수 있습니다 .


클래스를 확장한다고해서 클래스를 인스턴스화한다는 의미는 아닙니다. 실제로, 귀하의 경우 서브 클래스의 인스턴스를 작성하고 있습니다.

나는 추상 클래스가 시작을 허용하지 않는다고 확신합니다. 따라서 아니오라고 말하면 추상 클래스를 인스턴스화 할 수 없습니다. 그러나 확장 / 상속 할 수 있습니다.

추상 클래스를 직접 인스턴스화 할 수 없습니다. 그러나 클래스의 인스턴스 (원래 추상 클래스의 인스턴스가 아님)를 간접적으로 얻을 수는 없습니다. 원래 추상 클래스를 인스턴스화 할 수는 없지만 다음을 수행 할 수 있습니다.

  1. 빈 클래스 만들기
  2. 추상 클래스에서 상속
  3. 파생 된 클래스를 인스턴스화

따라서 파생 클래스 인스턴스를 통해 추상 클래스의 모든 메서드와 속성에 액세스 할 수 있습니다.


추상 클래스를 인스턴스화하는 것은 불가능합니다. 실제로 할 수있는 일은 추상 클래스에서 몇 가지 일반적인 메소드를 구현하고 다른 사람들은 구현하지 않도록하고 (추상 선언) 구체적인 후손이 필요에 따라 구현하도록하십시오. 그런 다음 팩토리를 만들 수 있습니다.이 추상 클래스의 인스턴스 (실제로 그의 구현 자)를 반환합니다. 그런 다음 공장에서 선택할 구현자를 결정합니다. 이것을 공장 설계 패턴이라고합니다.

   public abstract class AbstractGridManager {
        private LifecicleAlgorithmIntrface lifecicleAlgorithm;
        // ... more private fields

        //Method implemented in concrete Manager implementors 
        abstract public Grid initGrid();

        //Methods common to all implementors
        public Grid calculateNextLifecicle(Grid grid){
            return this.getLifecicleAlgorithm().calculateNextLifecicle(grid);
        }

        public LifecicleAlgorithmIntrface getLifecicleAlgorithm() {
            return lifecicleAlgorithm;
        }
        public void setLifecicleAlgorithm(LifecicleAlgorithmIntrface lifecicleAlgorithm) {
            this.lifecicleAlgorithm = lifecicleAlgorithm;
        }
        // ... more common logic and getters-setters pairs
    }

구체적 구현자는 abstract로 선언 된 메소드 만 구현하면되지만 abstract로 선언되지 않은 추상 클래스의 해당 클래스에서 구현 된 논리에 액세스 할 수 있습니다.

public class FileInputGridManager extends AbstractGridManager {

private String filePath;

//Method implemented in concrete Manager implementors 
abstract public Grid initGrid();

public class FileInputGridManager extends AbstractGridManager {

    private String filePath;

    //Method implemented in concrete Manager implementors 
    abstract public Grid initGrid();

    public Grid initGrid(String filePath) {
        List<Cell> cells = new ArrayList<>();
        char[] chars;
        File file = new File(filePath); // for example foo.txt
        // ... more logic
        return grid;
    }
}

그런 다음 공장은 다음과 같이 보입니다.

public class GridManagerFactory {
    public static AbstractGridManager getGridManager(LifecicleAlgorithmIntrface lifecicleAlgorithm, String... args){
        AbstractGridManager manager = null;

        // input from the command line
        if(args.length == 2){
            CommandLineGridManager clManager = new CommandLineGridManager();
            clManager.setWidth(Integer.parseInt(args[0]));
            clManager.setHeight(Integer.parseInt(args[1]));
            // possibly more configuration logic
            ...
            manager = clManager;
        } 
        // input from the file
        else if(args.length == 1){
            FileInputGridManager fiManager = new FileInputGridManager();
            fiManager.setFilePath(args[0]);
            // possibly more method calls from abstract class
            ...
            manager = fiManager ;
        }
        //... more possible concrete implementors
        else{
            manager = new CommandLineGridManager();
        }
        manager.setLifecicleAlgorithm(lifecicleAlgorithm);
        return manager;
    }
}

AbstractGridManager의 수신자는 그에 대한 메소드를 호출하고 자신이 얻은 구체적인 구현을 알지 못하고 구체적인 하위 클래스 (및 추상 클래스 메소드에서 부분적으로 구현)에서 논리를 가져옵니다. 이것은 제어 반전 또는 의존성 주입이라고도합니다.


아니요, 추상 클래스의 객체를 만들 수는 없지만 추상 클래스의 참조 변수를 만듭니다. 참조 변수는 파생 클래스 (Abstract 클래스의 하위 클래스)의 객체를 참조하는 데 사용됩니다.

이 개념을 설명하는 예는 다음과 같습니다.

abstract class Figure { 

    double dim1; 

    double dim2; 

    Figure(double a, double b) { 

        dim1 = a; 

        dim2 = b; 

    } 

    // area is now an abstract method 

    abstract double area(); 

    }


    class Rectangle extends Figure { 
        Rectangle(double a, double b) { 
        super(a, b); 
    } 
    // override area for rectangle 
    double area() { 
        System.out.println("Inside Area for Rectangle."); 
        return dim1 * dim2; 
    } 
}

class Triangle extends Figure { 
    Triangle(double a, double b) { 
        super(a, b); 
    } 
    // override area for right triangle 
    double area() { 
        System.out.println("Inside Area for Triangle."); 
        return dim1 * dim2 / 2; 
    } 
}

class AbstractAreas { 
    public static void main(String args[]) { 
        // Figure f = new Figure(10, 10); // illegal now 
        Rectangle r = new Rectangle(9, 5); 
        Triangle t = new Triangle(10, 8); 
        Figure figref; // this is OK, no object is created 
        figref = r; 
        System.out.println("Area is " + figref.area()); 
        figref = t; 
        System.out.println("Area is " + figref.area()); 
    } 
}

여기서는 Figure 유형의 개체를 만들 수 없지만 Figure 유형의 참조 변수를 만들 수 있습니다. 여기서는 그림 유형의 참조 변수를 작성했으며 그림 클래스 참조 변수는 Class Rectangle 및 Triangle의 오브젝트를 참조하는 데 사용됩니다.

참고 URL : https://stackoverflow.com/questions/13670991/can-we-instantiate-an-abstract-class



반응형