615

あるインタビューで、「抽象クラスをインスタンス化できるかどうか」と尋ねられました。

私の返事は「いいえ、できません」でした。しかし、インタビュアーは私に「間違っている、できる」と言った。

私はこれについて少し議論しました。それから彼は私にこれを家で自分で試すように言った。

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

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

ここでは、クラスのインスタンスを作成し、抽象クラスのメソッドを呼び出しています。誰かが私にこれを説明できますか?面接中に本当に間違っていたのですか?

4

16 に答える 16

771

ここで、クラスのインスタンスを作成しています

いいえ、ここでは抽象クラスのインスタンスを作成していません。むしろ、抽象クラスの匿名サブクラスのインスタンスを作成しています。そして、サブクラス objectを指す抽象クラス参照でメソッドを呼び出しています。

この動作は、JLS - セクション # 15.9.1に明確にリストされています: -

クラス インスタンス作成式がクラス本体で終了する場合、インスタンス化されるクラスは無名クラスです。それで:

  • T がクラスを表す場合、T によって名前が付けられたクラスの無名直接サブクラスが宣言されます。T で示されるクラスが最終クラスの場合、コンパイル時エラーになります。
  • T がインターフェイスを表す場合、T で指定されたインターフェイスを実装する Object の匿名直接サブクラスが宣言されます。
  • いずれの場合も、サブクラスの本体は、クラス インスタンス作成式で指定された ClassBody です。
  • インスタンス化されるクラスは無名サブクラスです。

鉱山を強調します。

また、JLS - セクション # 12.5では、オブジェクト作成プロセスについて読むことができます。ここで、その中から 1 つのステートメントを引用します。

新しいクラス インスタンスが作成されるたびに、そのクラス タイプで宣言されたすべてのインスタンス変数と、クラス タイプの各スーパークラスで宣言されたすべてのインスタンス変数 (非表示の可能性があるすべてのインスタンス変数を含む) のためのスペースを備えたメモリ空間が割り当てられます。

新しく作成されたオブジェクトへの参照が結果として返される直前に、指定されたコンストラクターが処理され、次の手順を使用して新しいオブジェクトが初期化されます。

私が提供したリンクで完全な手順について読むことができます。


インスタンス化されるクラスがAnonymous SubClassであることを実際に確認するには、両方のクラスをコンパイルするだけです。これらのクラスを 2 つの異なるファイルに入れるとします。

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

これらの番号は、それらの匿名クラスが含まれているクラスに表示される順序を示します。

于 2012-12-02T16:04:01.307 に答える
94

上記は、my抽象クラスのサブクラスである匿名内部クラスをインスタンス化します。抽象クラス自体をインスタンス化することと厳密には同じではありません。OTOH、すべてのサブクラス インスタンスはそのすべてのスーパー クラスとインターフェイスのインスタンスであるため、ほとんどの抽象クラスは具体的なサブクラスの 1 つをインスタンス化することによって実際にインスタンス化されます。

インタビュアーが「間違っている!」と言った場合。説明せずに、ユニークな反例としてこの例を挙げましたが、彼は自分が何について話しているのか分からないと思います.

于 2012-12-02T16:04:53.127 に答える
88

= my() {};: であるべきオブジェクトの単純なインスタンス化ではなく、匿名の実装があることを意味します= my()。抽象クラスをインスタンス化することはできません。

于 2012-12-02T16:29:18.867 に答える
30

Just observations you could make:

  1. Why poly extends my? This is useless...
  2. What is the result of the compilation? Three files: my.class, poly.class and poly$1.class
  3. If we can instantiate an abstract class like that, we can instantiate an interface too... weird...


Can we instantiate an abstract class?

No, we can't. What we can do is, create an anonymous class (that's the third file) and instantiate it.


What about a super class instantiation?

The abstract super class is not instantiated by us but by java.

EDIT: Ask him to test this

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());

}

output is:

false
class my$1
class my$2
于 2012-12-06T16:12:15.507 に答える
19

たった1行で簡単に答えることができます

いいえ、抽象クラスをインスタンス化することはできません

しかし、面接官はまだ同意しない場合は、面接官に伝えることができます

できることは、匿名クラスを作成することだけです。

そして、匿名クラスによると、クラスは同じ場所/行で宣言およびインスタンス化されます

したがって、インタビュアーは、あなたの信頼度と OOP についてどれだけ知っているかを確認することに関心を持つ可能性があります。

于 2012-12-07T04:09:10.577 に答える
13

抽象クラスはインスタンス化できませんが、サブクラス化することはできます。このリンクを見る

最良の例は

Calenderクラスには抽象メソッドgetInstance()がありますが、Calendar calc=Calendar.getInstance();

calcは、クラスGregorianCalendarのクラスインスタンスを「GregorianCalendarextendsCalendar」と呼んでいます

実際、匿名の内部型 を使用すると、抽象クラスの名前のないサブクラスとそのインスタンスを作成できます。

于 2012-12-03T09:14:11.727 に答える
12

テクニカルアンサー

抽象クラスはインスタンス化できません - これは定義と設計によるものです。

JLS、第8章から。クラス:

名前付きクラスは、abstract と宣言することができ (§8.1.1.1)、実装が不完全な場合は、abstract と宣言する必要があります。このようなクラスはインスタンス化できませんが、サブクラスによって拡張できます。

Classes.newInstance() の JSE 6 Java doc から:

InstantiationException - この Class が抽象クラス、インタフェース、配列クラス、プリミティブ型、または void を表している場合。または、クラスに nullary コンストラクターがない場合。または、インスタンス化が他の理由で失敗した場合。

もちろん、抽象クラス (無名サブクラスを含む) の具象サブクラスをインスタンス化し、抽象型へのオブジェクト参照の型キャストを実行することもできます。

これに関する別の角度 - チームプレイとソーシャル インテリジェンス:

この種の技術的な誤解は、現実の世界で複雑な技術や法的仕様を扱う際に頻繁に発生します。

ここでは「技術力」よりも「人力」が重要になることがあります。競争的かつ積極的に議論のあなたの側を証明しようとする場合、理論的には正しいかもしれませんが、戦いをする/「顔」にダメージを与える/敵を作成することで、価値がある以上のダメージを与えることもできます. 意見の相違を解決する際には、和解し、理解してください。誰が知っていますか - おそらくあなたは「両方とも正しい」かもしれませんが、用語のわずかに異なる意味に取り組んでいます??

誰が知っていますか - 可能性は低いですが、面接官があなたを困難な状況に追い込み、感情的および社会的にどのように振る舞うかを見るために、意図的に小さな衝突/誤解を導入した可能性があります. 同僚に対して親切かつ建設的に対応し、先輩からのアドバイスに従い、面接後もメールまたは電話で問題や誤解を解決してください。やる気があり、細部に気を配っていることを示します。

于 2013-03-18T03:10:59.217 に答える
4

いいえ、抽象クラスをインスタンス化することはできません。匿名クラスのみをインスタンス化します。抽象クラスでは、抽象メソッドを宣言し、具象メソッドのみを定義します。

于 2014-01-04T07:24:44.683 に答える
4

クラスを拡張しても、クラスをインスタンス化しているわけではありません。実際、あなたの場合、サブクラスのインスタンスを作成しています。

抽象クラスでは開始できないと確信しています。ですから、私はノーと言いたいです: 抽象クラスをインスタンス化することはできません。ただし、拡張/継承することはできます。

抽象クラスを直接インスタンス化することはできません。ただし、クラスのインスタンス (実際には元の抽象クラスのインスタンスではない) を間接的に取得できないという意味ではありません。つまり、元の抽象クラスをインスタンス化することはできませんが、次のことができます。

  1. 空のクラスを作成する
  2. 抽象クラスから継承
  3. 派生クラスをインスタンス化する

したがって、派生クラスのインスタンスを介して、抽象クラスのすべてのメソッドとプロパティにアクセスできます。

于 2013-11-03T15:39:24.907 に答える
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 の受信者は、彼のメソッドを呼び出して、具体的なディセンダー (および一部は抽象クラス メソッド) に実装されたロジックを取得しますが、取得した具体的な実装が何であるかを知りません。これは、制御の反転または依存性注入とも呼ばれます。

于 2014-06-03T16:11:27.513 に答える