インタビュアーは尋ねました - 抽象クラスをインスタンス化できますか?私は言いました。いいえ、彼は私に言いました - 間違っています。
私はこれについて少し議論しました。それから彼は私にあなたの家でこれを自分で試すように言った。
abstract class my {
public void mymethod() {
System.out.print("Abstract");
}
}
class poly {
public static void main(String a[]) {
my m = new my() {};
m.mymethod();
}
}
ここでは、自分のクラスのインスタンスを作成し、抽象クラスのメソッドを呼び出します。誰か私にこれを説明してもらえますか?インタビューの間に私は本当に間違っていましたか?
ここで、私は私のクラスのインスタンスを作成しています
いいえ、ここで抽象クラスのインスタンスを作成していません。むしろ、あなたは抽象クラスの匿名サブクラスのインスタンスを作成しているのです。そして、あなたは、あなたのabstract class参照の上でサブクラスオブジェクトを指すメソッドを呼び出しています。
この振る舞いは JLS - Section#15.9.1 - に明確に記載されています : -
クラスインスタンス作成式がクラス本体で終わっている場合、インスタンス化されているクラスは匿名クラスです。その後:
- Tがクラスを表す場合は、Tで指定されたクラスの無名直接サブクラスが宣言されます。 Tで示されるクラスが最終クラスの場合、コンパイル時エラーです。
- Tがインタフェースを表す場合、Tで指定されたインタフェースを実装するObjectの無名直接サブクラスが宣言されます。
- どちらの場合も、サブクラスの本体は、クラスインスタンス作成式で指定されたClassBodyです。
- インスタンス化されているクラスは匿名サブクラスです。
私のことを強調します。
また、 JLS - Section 12.5 で、Object Creation Processについて読むことができます。私はここからそれから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
これらの数字は、それらの匿名クラスが外側のクラスに現れる順番を表します。
上記は、my
抽象クラスのサブクラスである無名内部クラスをインスタンス化します。抽象クラス自体をインスタンス化することと厳密には同じではありません。 OTOHでは、すべてのサブクラスのインスタンスはそのすべてのスーパークラスおよびインタフェースのインスタンスなので、ほとんどの抽象クラスは実際にはそれらの具象サブクラスの1つをインスタンス化することによってインスタンス化されます。
インタビュアーが「間違っている」と言っただけの場合説明せずにこの例を挙げて、ユニークな反例として、彼が何について話しているのかわからないと思います。
= my() {};
は、オブジェクトの単純なインスタンス化ではなく、= my()
という匿名の実装があることを意味します。抽象クラスをインスタンス化することはできません。
あなたがすることができるだけの観察:
poly
がmy
を拡張するのですか?これは無駄です...my.class
、poly.class
およびpoly$1.class
抽象クラスをインスタンス化できますか?
いいえ、できません。できることは、匿名クラス(これが3番目のファイルです)を作成してインスタンス化することです。
スーパークラスのインスタンス化はどうですか?
抽象スーパークラスは us ではなくJavaによってインスタンス化されます。
編集:これをテストするように彼に依頼してください
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
1行で簡単に答えることができます
いいえ 、抽象クラスをインスタンス化することはできません
しかし、インタビュアーはまだ同意しません、それからあなたは彼/彼女に言うことができます
できることは、匿名クラスを作成できることだけです。
そして、Anonymousクラスによると、 クラスは同じ場所で宣言されインスタンス化されます/ line
ですから、インタビュアーはあなたの自信度とあなたがOOPについてどれだけ知っているかをチェックすることに興味があるでしょう。
技術的な部分は他の回答で十分にカバーされています、そしてそれは主に以下で終わります:
「彼は間違っている、彼はものを知っていない、彼にSOに加わってそしてそれをすべてクリアさせるように頼む:)
私はこれが ストレス質問 であり、多くのインタビュアーがあなたについてもっと知り、あなたが困難で珍しいことにどのように反応するかについての重要なツールであるという事実(他の答えで言及された)状況あなたに間違ったコードを与えることによって、彼は おそらく あなたが反論したかどうかを見たがっていました。あなたがこれと同じような状況であなたの先輩に立ち向かう自信があるかどうかを知るため。
P.S:理由はわかりませんが、インタビュアーがこの記事を読んだという気がします。
抽象クラスはインスタンス化できませんが、サブクラス化できます。 このリンクを参照してください
最も良い例は
カレンダクラスには抽象メソッドgetInstance() がありますが、Calendar calc=Calendar.getInstance();
と言うと
calcはGregorianCalendarクラスのクラスインスタンスを "GregorianCalendarはCalendar を拡張する"として参照しています。
Infact 無名内部型 抽象クラス とそのインスタンスの名前なしサブクラスを作成することができます。
テクニカルアンサー
抽象クラスはインスタンス化できません - これは定義と設計によるものです。
JLSから、第8章。
名前付きクラスはabstract(8.1.1.1)と宣言することができ、不完全に実装されている場合はabstractと宣言しなければならない。そのようなクラスはインスタンス化できませんが、サブクラスによって拡張できます。
Classes.newInstance()に関するJSE 6 Javaドキュメントより:
InstantiationException - このClassが抽象クラス、インタフェース、配列クラス、プリミティブ型、またはvoidを表す場合または、クラスにnullコンストラクタがない場合または他の理由でインスタンス化が失敗した場合
もちろん、抽象クラスの具体的なサブクラス(無名サブクラスを含む)をインスタンス化したり、抽象型へのオブジェクト参照の型キャストを実行することもできます。
これとは異なる角度 - チームプレイとソーシャルインテリジェンス:
このような技術的な誤解は、複雑な技術や法的な仕様を扱うときに現実の世界で頻繁に起こります。
ここでは「人のスキル」が「技術のスキル」よりも重要になる可能性があります。あなたの主張を競争的にそして積極的に証明しようとするならば、理論的には正しいかもしれませんが、戦いをすること/「顔」を傷つけること/敵を作ることにおいてそれは価値があります。あなたの違いを解決する際に和解と理解をしてください。誰が知っている - 多分あなたは "両方"正しいですが、用語のわずかに異なる意味で働いていますか?
誰もが知っています - そうではありませんが、インタビュアーが意図的に小さな対立/誤解を招き、あなたを困難な状況に陥れ、感情的にも社会的にも行動しているのを見ることができます。同僚との親切で建設的な態度を取り、先輩からのアドバイスに従い、面接の後に続けて、電子メールまたは電話での課題や誤解を解決します。あなたはやる気があり、細部が重視されています。
abstract class
は、誰もが答えたときにnotをインスタンス化できるということは、十分に確立された事実です。
プログラムが匿名クラスを定義すると、コンパイラは実際に異なる名前で新しいクラスを作成します(パターンはEnclosedClassName$n
で、n
は匿名クラス番号です)
したがって、この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
キーワードを使用して無名クラスのインスタンスを作成できます。
クラスを拡張しても、そのクラスをインスタンス化しているわけではありません。実際には、あなたの場合あなたはサブクラスのインスタンスを作成しています。
抽象クラスでは開始が許可されていないと確信しています。だから、私はノーと言うでしょう:あなたは抽象クラスをインスタンス化することはできません。しかし、あなたはそれを拡張/継承することができます。
抽象クラスを直接インスタンス化することはできません。しかし、それはあなたが間接的にクラスのインスタンス(実際には元の抽象クラスのインスタンスではない)を取得できないという意味ではありません。私はあなたが元の抽象クラスをインスタンス化することはできませんがあなたができることを意味します:
そのため、派生クラスのインスタンスを介して抽象クラスのすべてのメソッドとプロパティにアクセスできます。
いいえ、抽象クラスのオブジェクトは作成できませんが、抽象クラスの参照変数は作成します。参照変数は、派生クラス(抽象クラスのサブクラス)のオブジェクトを参照するために使用されます。
これはこの概念を説明する例です
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型の参照変数は作成できます。ここで、Figure型の参照変数を作成し、Figureクラス参照変数を使用して、Class RectangleおよびTriangleのオブジェクトを参照します。
抽象クラスをインスタンス化することは不可能です。本当にできることは、いくつかの一般的なメソッドを抽象クラスに実装し、他のものを未実装(abstractと宣言)にして、具体的な子孫にそれらのニーズに応じて実装させることです。それからあなたはファクトリを作ることができます。そして、それはこの抽象クラス(実際には彼の実装者)のインスタンスを返します。工場では、どの実装者を選択するかを決定します。これはファクトリデザインパターンとして知られています:
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の受信側は、自分のメソッドを呼び出して、取得した具体的な実装が何であるかを知らずに、具象ディセンダ(および抽象クラスメソッド)に実装されたロジックを取得します。これは、制御の反転または依存性注入としても知られています。