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


573

인터뷰 중 하나에서 "추상 수업을 인스턴스화 할 수 있을까요?"라는 질문을 받았습니다.

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

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

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

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

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


2
약간 관련이 있지만, 하나는 아마 C ++의 추상 클래스를 인스턴스화 할 수 있습니다 :가 아닌 추상 클래스를 파생시키는 경우 B추상적 인 한에서 A, 건설 부분에서B실행으로 구성되어 인스턴스 A의 생성자를, 객체의 실행시의 형태는 사실이다 A. 그러나 일시적입니다.
Vlad

8
@jWeavers : 그가 제시 한 예는 완전히 틀 렸습니다. "그러면 추상 클래스의 용도는 무엇입니까?" 확장하는 경우 확장 클래스의 인스턴스를 만드는 이유는 무엇입니까? 데이터가 전혀없는 완전히 새로운 물체입니다.
Lemon Juice

3
또는 면접관이 자신이 제안한 내용에 대해 자신의 진술에 대해 얼마나 확신하는지 확인하고 싶을 수도 있습니다!
Sid

5
그는 당신에게 거짓말을했습니다. 이 코드가하는 것이 아니라 익명의 서브 클래스가 무엇인지 설명하지 못했을 때 공을 떨어 뜨 렸습니다. 그는 이미 알고 있었으며 당신이 알고 있는지 알고 싶었습니다.
candied_orange

2
이것은 퀴즈 쇼가 아니라 면접이었습니다. Java 또는 C ++에서 추상 클래스를 인스턴스화 할 수 있다면 어떨까요? 영리한 일이 아니기 때문에 그렇게하지 않을 것입니다. Objective-C에서 추상 클래스는 규칙에 의해서만 추상이며 클래스를 인스턴스화하는 것은 버그입니다.
gnasher729

답변:


722

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

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

이 동작은 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

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


172
@ 코더. 정확한 답변은 다음과 같습니다.-추상 클래스를 인스턴스화 할 수 없지만 추상 클래스의 구체적인 하위 클래스를 인스턴스화 할 수 있습니다.
Rohit Jain

16
한 줄에 다음과 같이 말할 수 있습니다 .- 추상 클래스를 인스턴스화 할 수 없습니다. 그것이 추상 클래스의 목적입니다.
Rahul Tripathi

66
면접관이 당신보다 자신의 답변에 더 많은 투자를 한 것 같습니다.
Neil T.

7
또 다른 의견 에 따르면 ( JLS 참조 포함 ) "객체는 해당 클래스의 클래스와 해당 클래스의 모든 수퍼 클래스의 인스턴스라고합니다."따라서 실제로 기술적으로 추상 클래스의 인스턴스를 생성하지 않습니까? 즉, 추상 클래스를 인스턴스화합니까?
arshajii

6
@ARS가 나는 인 차이가 있다고 말하고 싶지만 instance of하고 instantiating. 하나의 클래스 만 인스턴스화하는 반면, 생성 한 객체는 상속으로 인해 여러 클래스의 인스턴스가 될 수 있습니다.
Simon Forsberg

89

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

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


10
엄밀히 말하면 추상 수퍼 클래스는 인스턴스화되지 않습니다. 인스턴스 변수를 초기화하기 위해 생성자가 호출됩니다.
Perception 인식

4
그렇습니다. subclassInstance instanceof SuperClasstrue를 반환하므로 객체가 수퍼 클래스의 인스턴스이므로 수퍼 클래스가 설정되었음을 의미합니다. 그러나 그것은 의미 론적 nitpicking입니다.
JB 니 제트

5
실제로 의미론 일 수 있습니다. Java는 새로운 키워드 를 통해 객체를 생성 하는 관점에서 인스턴스화 를 정의 합니다 (이는 추상 클래스로는 수행 할 수 없음). 물론 구체적인 하위 클래스는 부모 계층의 모든 멤버 인스턴스를 올바르게보고합니다.
Perception

11
JLS의 4.12.6 절 은 "객체는 그 클래스와 그 클래스의 모든 슈퍼 클래스의 인스턴스라고한다"고 말합니다.
JB 니 제트

85

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


30

당신이 할 수있는 관찰 :

  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

관찰 3 일 : 예, 우리는 할 수있다 Serializable s = new Serializable() {};(꽤 쓸모가있는) 및 태그 경우 코드 줄 것이다에 class my$3(또는 어떤 클래스와 수를 둘러싸)
분석 재개 모니카 - notmaynard

18

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

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

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

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

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

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


17

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

나는 이것이 다른 질문에 언급되었을 것이라는 사실을 강조 하고 싶습니다. 이것은 스트레스 질문 일 수 있으며 많은 면접관이 당신에 대해 더 많이 알 수있는 중요한 도구이며 어렵고 특이한 상황에 어떻게 대처 하는가입니다. 당신에게 잘못된 코드를 제공함으로써, 아마도 당신이 말다툼했는지를 알고 싶었을 것입니다 . 이와 비슷한 상황에서 선배들과 대항 할 자신이 있는지를 알기 위해.

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


13

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

가장 좋은 예는

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

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

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


11

기술 답변

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

JLS 8 장에서 제공합니다.

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

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

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

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

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

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

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

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


7

모든 사람이 대답했을 때 인스턴스화 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();
    }
}

4

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


4

추상 클래스

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

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


3

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


3

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

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

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

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

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


2

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

   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의 수신자는 자신에게 메소드를 호출하고 자신이 얻은 구체적인 구현이 무엇인지 모른 채 구체적인 하위 클래스 (및 추상 클래스 메소드로 부분적으로 구현)에서 논리를 가져옵니다. 이것은 제어 반전 또는 의존성 주입이라고도합니다.


2

아니요, 추상 클래스의 객체를 만들 수는 없지만 추상 클래스의 참조 변수를 만듭니다. 참조 변수는 파생 클래스 (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의 오브젝트를 참조하는 데 사용됩니다.


0

실제로 추상 클래스의 객체를 직접 만들 수는 없습니다. 우리가 만드는 것은 추상 호출의 참조 변수입니다. 참조 변수는 Abstract 클래스를 상속하는 클래스의 객체, 즉 추상 클래스의 하위 클래스를 참조하는 데 사용됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.