Javaの抽象クラス


274

Javaの「抽象クラス」とは何ですか?


35
+1この質問は非常に基本的で根本的なものであり、SOの定番です。これまでここで質問されたことがないことに驚いています。
ユヴァル

6
クレメントのコメントは-1(できれば)。lmgtfyは役に立ちません。理由については、たとえばこのmeta.stackexchange.com/questions/5280/embrace-the-non-googlersを
Jonik

26
@tuergeist。それが以前にSOで尋ねられたことがない限り、それがグーグルにとって簡単であるかどうかは関係ありません。また、プログラミング言語に関する初心者の質問はSOに属さないと誰が言っていますか?
Jonik、2009

12
私がSOについて好きなことの1つは、Webの他の部分で通常のBSが見つからない場合でも、凝縮された適切な回答が得られることです。まあ、とにかくそのようなものです。質問の+1!
アンダースハンソン、

1
SOは長い尾を持つだけではありません!J&Jはポッドキャスト56についてこれについて話しさえしています...
kwutchak '24

回答:


342

抽象クラスは、インスタンス化できないクラスです。抽象クラスは、インスタンス化できる継承サブクラスを作成することによって使用されます。抽象クラスは、継承するサブクラスに対していくつかのことを行います。

  1. 継承するサブクラスで使用できるメソッドを定義します。
  2. 継承するサブクラスが実装する必要がある抽象メソッドを定義します。
  3. サブクラスを他のすべてのサブクラスと交換できるようにする共通のインターフェースを提供します。

次に例を示します。

abstract public class AbstractClass
{
    abstract public void abstractMethod();
    public void implementedMethod() { System.out.print("implementedMethod()"); }
    final public void finalMethod() { System.out.print("finalMethod()"); }
}

「abstractMethod()」にはメソッド本体がないことに注意してください。このため、次のことはできません。

public class ImplementingClass extends AbstractClass
{
    // ERROR!
}

実装するメソッドはありませんabstractMethod()!したがって、JVMがのようなものになったときに何をすべきかを知る方法はありませんnew ImplementingClass().abstractMethod()

ここに正しいImplementingClassです。

public class ImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("abstractMethod()"); }
}

implementedMethod()またはを定義する必要がないことに注意してくださいfinalMethod()。それらはすでにによって定義されていAbstractClassます。

ここに別の正しいImplementingClassです。

public class ImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("abstractMethod()"); }
    public void implementedMethod() { System.out.print("Overridden!"); }
}

この場合、を上書きしましたimplementedMethod()

ただし、finalキーワードのため、次のことはできません。

public class ImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("abstractMethod()"); }
    public void implementedMethod() { System.out.print("Overridden!"); }
    public void finalMethod() { System.out.print("ERROR!"); }
}

finalMethod()in の実装がAbstractClassの最終実装としてマークされているため、これを行うことはできませんfinalMethod()。他の実装は許可されません。

これで、抽象クラスを2回実装することできます。

public class ImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("abstractMethod()"); }
    public void implementedMethod() { System.out.print("Overridden!"); }
}

// In a separate file.
public class SecondImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("second abstractMethod()"); }
}

これでどこかに別のメソッドを書くことができます。

public tryItOut()
{
    ImplementingClass a = new ImplementingClass();
    AbstractClass b = new ImplementingClass();

    a.abstractMethod();    // prints "abstractMethod()"
    a.implementedMethod(); // prints "Overridden!"     <-- same
    a.finalMethod();       // prints "finalMethod()"

    b.abstractMethod();    // prints "abstractMethod()"
    b.implementedMethod(); // prints "Overridden!"     <-- same
    b.finalMethod();       // prints "finalMethod()"

    SecondImplementingClass c = new SecondImplementingClass();
    AbstractClass d = new SecondImplementingClass();

    c.abstractMethod();    // prints "second abstractMethod()"
    c.implementedMethod(); // prints "implementedMethod()"
    c.finalMethod();       // prints "finalMethod()"

    d.abstractMethod();    // prints "second abstractMethod()"
    d.implementedMethod(); // prints "implementedMethod()"
    d.finalMethod();       // prints "finalMethod()"
}

タイプを宣言しbたにもかかわらずAbstractClass、が表示されることに注意してください"Overriden!"。これは、インスタンス化したオブジェクトが実際にImplementingClassはでimplementedMethod()あり、もちろんオーバーライドされているためです。(これは多型と呼ばれているのを見たかもしれません。)

特定のサブクラスに固有のメンバーにアクセスする場合は、最初にそのサブクラスにキャストダウンする必要があります。

// Say ImplementingClass also contains uniqueMethod()
// To access it, we use a cast to tell the runtime which type the object is
AbstractClass b = new ImplementingClass();
((ImplementingClass)b).uniqueMethod();

最後に、次のことはできません。

public class ImplementingClass extends AbstractClass, SomeOtherAbstractClass
{
    ... // implementation
}

一度に拡張できるクラスは1つだけです。複数のクラスを拡張する必要がある場合、それらはインターフェースでなければなりません。あなたはこれを行うことができます:

public class ImplementingClass extends AbstractClass implements InterfaceA, InterfaceB
{
    ... // implementation
}

以下はインターフェースの例です。

interface InterfaceA
{
    void interfaceMethod();
}

これは基本的に次と同じです:

abstract public class InterfaceA
{
    abstract public void interfaceMethod();
}

唯一の違いは、2番目の方法では、それが実際にインターフェースであることをコンパイラーに知らせないことです。これは、インターフェースを実装するだけで他のユーザーは実装しないようにする場合に役立ちます。ただし、一般的な初心者の経験則として、抽象クラスに抽象メソッドしかない場合は、おそらくそれをインターフェイスにする必要があります。

以下は違法です:

interface InterfaceB
{
    void interfaceMethod() { System.out.print("ERROR!"); }
}

インターフェイスにメソッドを実装することはできません。つまり、2つの異なるインターフェースを実装する場合、それらのインターフェースの異なるメソッドは衝突できません。インターフェースのすべてのメソッドは抽象なので、メソッドを実装する必要があり、メソッドは継承ツリーの唯一の実装であるため、コンパイラーはメソッドを使用する必要があることを認識しています。


5
ステートメントc.implementedMethod();の誤った説明については@Imagist -1 //「implementedMethod()」を出力し、「Overriden!」を出力します 常に
Sachin Kumar

2
@Sachinなぜ「implementedMethod()」が出力されるのか理解するのに30分も無駄に費やしましたが、あなたのコメントを見ました。Javaで何か変更があったのか、それとも他の人が間違いを見落としたのか?
Rounak 2015年

@SachinKumar作者の反応がないため、このエラーを修正することにしました。CMIIW。
Mateen Ulhaq、2016年

@SachinKumar私はここのゲームに少し遅れましたが、C ++ヘッダーファイルでのメソッド宣言(ただし実装ではない)とよく似ていると思いますか?
Schwaitz 2017

5
@SachinKumarなぜc.implementedMethod()「Overriden!」と印刷するのですか?SecondImplementingClassオーバーライドしませんimplementedMethod()
John Red

75

Javaクラスは、次の条件下で抽象になります。

1.少なくとも1つのメソッドが抽象としてマークされている:

public abstract void myMethod()

その場合、コンパイラーはクラス全体を抽象としてマークするように強制します。

2.クラスは抽象としてマークされます。

abstract class MyClass

すでに述べたように:抽象メソッドがある場合、コンパイラーはクラス全体を抽象としてマークするように強制します。ただし、抽象メソッドがない場合でも、クラスを抽象としてマークできます。

一般的な使用:

抽象クラスの一般的な用途は、インターフェースと同様に、クラスの概要を提供することです。ただし、インターフェイスとは異なり、すでに機能を提供できます。つまり、クラスの一部が実装され、一部はメソッド宣言で概説されているだけです。("概要")

抽象クラスはインスタンス化できませんが、抽象クラスに基づいて具象クラスを作成し、インスタンス化することができます。そのためには、抽象クラスから継承して、抽象メソッドをオーバーライドする、つまり実装する必要があります。


1
Nitpick:明示的に抽象として宣言されているクラスでのみ抽象メソッドを宣言できるため、2番目の「条件」は冗長です。
スティーブンC

2
同意しました、アドバイスは本当に正確ではなく、よく書かれていません、それはちょうどうまくフォーマットされています。
正午シルク

しかし、「クラスを具体化する」というあなたの忠告もまた間違った言葉で書かれています。あなたはしていない作り、それは抽象的だかいないかによって、それはどちらかである、またはありませんが、クラスのコンクリートを。
正午シルク

1
これはまったく間違っています。抽象クラスは抽象メソッドを持つ必要はありません。メソッドなしで、または具象メソッドのみで抽象クラスを作成できます。
ジョーン

1
ゲームに10年遅れましたが、これが最も正確な答えです。@Jornあなたは私が思う答えと混同しています。abstractクラスが抽象的であるために必要なのはキーワードだけであることは暗に示されています。ただし、具象クラスにメソッドを含めることはできません。したがって、クラスにメソッドがある場合は、コンパイラーに対してクラスとして宣言する必要があります。abstract abstractabstract
Rakib

24

abstractキーワードを使用して宣言されたクラスは、と呼ばれabstract classます。抽象化は、データ実装の詳細を隠し、機能のみをユーザーに表示するプロセスです。抽象化を使用すると、オブジェクトの動作ではなく、オブジェクトの動作に焦点を合わせることができます。

抽象クラスの主なもの

  • 抽象クラスには、抽象メソッドが含まれる場合と含まれない場合があります。非抽象メソッドが存在する場合があります。

    抽象メソッドは、次のように、実装なし(中括弧なし、セミコロンが続く)で宣言されたメソッドです。

    例: abstract void moveTo(double deltaX, double deltaY);

  • クラスに少なくとも1つの抽象メソッドがある場合、そのクラスは抽象でなければなりません

  • 抽象クラスがインスタンス化されていない可能性があります(抽象クラ​​スのオブジェクトの作成は許可されていません)

  • 抽象クラスを使用するには、それを別のクラスから継承する必要があります。その中のすべての抽象メソッドに実装を提供します。

  • 抽象クラスを継承する場合は、その中のすべての抽象メソッドに実装を提供する必要があります。

抽象クラスの宣言宣言abstract時にクラスの前にキーワードを 指定すると、抽象化されます。以下のコードを見てください:

abstract class AbstractDemo{ }

抽象メソッドの宣言宣言abstract時にメソッドの前にキーワードを 指定すると、抽象メソッドになります。以下のコードを見てください、

abstract void moveTo();//no body

クラスを抽象化する必要がある理由

オブジェクト指向の描画アプリケーションでは、円、長方形、線、ベジェ曲線、およびその他の多くのグラフィックオブジェクトを描画できます。これらのオブジェクトはすべて、特定の状態(例-の場合:位置、方向、線の色、塗りつぶしの色)と動作(例-の場合:moveTo、回転、サイズ変更、描画)が共通しています。これらの状態と動作の一部は、すべてのグラフィックオブジェクトで同じです(例:塗りつぶしの色、位置、およびmoveTo)。その他は異なる実装が必要です(例:サイズ変更または描画)。すべてのグラフィックオブジェクトは、それ自体を描画またはサイズ変更できる必要があります。それらの方法が異なるだけです。

これは、抽象スーパークラスに最適な状況です。GraphicObject次の図に示すように、類似点を利用して、同じ抽象親オブジェクト(ex:の場合)から継承するようにすべてのグラフィックオブジェクトを宣言できます。 ここに画像の説明を入力してください

最初に、抽象クラスを宣言しGraphicObjectて、現在の位置やmoveToメソッドなど、すべてのサブクラスで完全に共有されるメンバー変数とメソッドを提供します。GraphicObjectまた、すべてのサブクラスで実装する必要があるが、さまざまな方法で実装する必要がある、drawまたはresizeなどの抽象メソッドも宣言しました。GraphicObjectクラスは、このような何かを見ることができます:

abstract class GraphicObject {

  void moveTo(int x, int y) {
    // Inside this method we have to change the position of the graphic 
    // object according to x,y     
    // This is the same in every GraphicObject. Then we can implement here. 
  }

  abstract void draw(); // But every GraphicObject drawing case is 
                        // unique, not common. Then we have to create that 
                        // case inside each class. Then create these    
                        // methods as abstract 
  abstract void resize();
}

サブクラスでの抽象メソッドの使用法およびGraphicObjectなどのの非抽象サブクラスはそれぞれ、およびメソッドの実装を提供する必要があります。CircleRectangledrawresize

class Circle extends GraphicObject {
  void draw() {
    //Add to some implementation here
  }
  void resize() {
    //Add to some implementation here   
  }
}
class Rectangle extends GraphicObject {
  void draw() {
    //Add to some implementation here
  }
  void resize() {
    //Add to some implementation here
  }
}

mainメソッド内では、次のようにすべてのメソッドを呼び出すことができます。

public static void main(String args[]){
   GraphicObject c = new Circle();
   c.draw();
   c.resize();
   c.moveTo(4,5);   
}

Javaで抽象化を実現する方法

Javaで抽象化を実現するには2つの方法があります

  • 抽象クラス(0〜100%)
  • インターフェース(100%)

コンストラクター、データメンバー、メソッドなどを含む抽象クラス

abstract class GraphicObject {

  GraphicObject (){
    System.out.println("GraphicObject  is created");
  }
  void moveTo(int y, int x) {
       System.out.println("Change position according to "+ x+ " and " + y);
  }
  abstract void draw();
}

class Circle extends GraphicObject {
  void draw() {
    System.out.println("Draw the Circle");
  }
}

class TestAbstract {  
 public static void main(String args[]){

   GraphicObject  grObj = new Circle ();
   grObj.draw();
   grObj.moveTo(4,6);
 }
}

出力:

GraphicObject  is created
Draw the Circle
Change position according to 6 and 4

2つのルールを覚えておいてください。

  • クラスに抽象メソッドと具象メソッドがほとんどない場合は、クラスとして宣言しabstractます。

  • クラスに抽象メソッドしかない場合は、それをとして宣言しinterfaceます。

参照:


上記の例、以下の例、および以下の例の出力で、moveToのパラメーターxとyの順序が異なるのはなぜですか?インターフェースや抽象クラスなどの概念の重要性を説明しようとしているのであれば、一貫して実装または拡張しているインターフェースや抽象クラスと同じ関数シグネチャを使用すべきではないでしょうか。
Jonathan Rys、2018

2つのルールで
解決

4

これはインスタンス化できないクラスであり、実装しているクラスに、おそらくそれが概説している抽象メソッドを実装するよう強制します。


3

簡単に言えば、抽象クラスは、もう少し機能の多いインターフェースのようなものと考えることができます。

抽象クラスを保持するインターフェイスをインスタンス化することはできません。

あなたのインターフェイスでは、あなただけのメソッドヘッダを定義することができ、実装のすべてがされて強制的に実行するために、すべての それらのを。抽象クラスでは、メソッドヘッダーを定義することもできますが、ここで(インターフェイスの違いに対して)、メソッドの本体(通常はデフォルトの実装)を定義することもできます。さらに、他のクラスが(実装ではなく、したがって、子クラスごとに1 だけの抽象クラスを持つこともできる)拡張クラスである場合、抽象メソッドを指定しない限り、抽象クラスのすべてのメソッドを実装する必要はありません(そのような場合、それはインターフェースのように機能し、メソッド本体を定義することはできません)。

public abstract class MyAbstractClass{
  public abstract void DoSomething();
}

それ以外の場合、抽象クラスの通常のメソッドの場合、「継承者」は通常どおり、デフォルトの動作を使用するか、それをオーバーライドできます。

例:

public abstract class MyAbstractClass{

  public int CalculateCost(int amount){
     //do some default calculations
     //this can be overriden by subclasses if needed
  }

  //this MUST be implemented by subclasses
  public abstract void DoSomething();
}

OPがインターフェイスとは何かを知らない場合、この回答は役に立ちません。抽象クラスとインターフェースは相互に関連しているため、OPが一方を認識せずに他方を認識することはほとんどありません。
イマジスト、

しかし、それは可能性があります。それは彼がインターフェイスが何であるか、そしてそれがどのように機能するかを知っているだけかもしれません、それから彼は抽象クラスに出くわし、なぜそれらが必要なのか疑問に思います。それはできませんでしたか?
ジュリ

3

オラクルのドキュメントから

抽象メソッドとクラス:

抽象クラスは、抽象として宣言されたクラスです。抽象メソッドが含まれる場合と含まれない場合があります。

抽象クラスはインスタンス化できませんが、サブクラス化できます

抽象メソッドは、次のように、実装なし(中括弧なし、セミコロンが続く)で宣言されたメソッドです

abstract void moveTo(double deltaX, double deltaY);

クラスに抽象メソッドが含まれている場合、次のようにクラス自体を抽象として宣言する必要があります。

public abstract class GraphicObject {
   // declare fields
   // declare nonabstract methods
   abstract void draw();
}

抽象クラスがサブクラス化されると、サブクラスは通常、親クラスのすべての抽象メソッドの実装を提供します。ただし、そうでない場合は、サブクラスもabstractとして宣言する必要があります

以来abstract classesinterfaces関連している、SEの質問以下を見て:

インターフェイスと抽象クラスの違いは何ですか?

インターフェイスと抽象クラスの違いをどのように説明すればよいですか?


3

ここで答えを入手してください:

抽象クラスとJavaのインターフェース

抽象クラスはfinalメソッドを持つことができますか?

ところで-それらはあなたが最近尋ねた質問です。評判を築くための新しい質問について考えてください...

編集:

この投稿者と参照された質問の投稿者の名前は同じか、少なくとも似ていますが、ユーザーIDは常に異なることに気づきました。したがって、技術的な問題があり、keyurが再度ログインして質問への回答を見つけるのに問題があるか、またはこれはSOコミュニティを楽しませるゲームの一種です;)


そして、それが私が「コミュニティウィキ」をチェックした理由です-それらの質問に反応することによって評判を上げるべきではありません;)
Andreas

1

これらすべての投稿に少し追加。

クラスを宣言しても、そのクラスに属するすべてのメソッドを定義する方法がわからない場合があります。たとえば、Writerというクラスを宣言して、それにwrite()というメンバーメソッドを含める ことができます。ただし、write()のコーディング方法は、Writerデバイスのタイプごとに異なるため、わかりません。もちろん、プリンター、ディスク、ネットワーク、コンソールなどのWriterのサブクラスを派生させることでこれを処理する予定です。


1

抽象クラスは直接インスタンス化することはできませんが、使用できるようにから派生する必要があります。抽象メソッドが含まれている場合、クラス抽象でなければなりません:直接

abstract class Foo {
    abstract void someMethod();
}

または間接的に

interface IFoo {
    void someMethod();
}

abstract class Foo2 implements IFoo {
}

ただし、クラスは抽象メソッドを含まずに抽象クラスにすることができます。直接のインスタンス化を防ぐためのその方法、例えば

abstract class Foo3 {
}

class Bar extends Foo3 {

}

Foo3 myVar = new Foo3(); // illegal! class is abstract
Foo3 myVar = new Bar(); // allowed!

後者の抽象クラスのスタイルは、「インターフェースのような」クラスを作成するために使用できます。インターフェイスとは異なり、抽象クラスには非抽象メソッドとインスタンス変数を含めることができます。これを使用して、クラスを拡張するための基本機能を提供できます。

もう1つの頻繁なパターンは、抽象クラスに主要な機能を実装し、拡張クラスによって実装される抽象メソッドにアルゴリズムの一部を定義することです。愚かな例:

abstract class Processor {
    protected abstract int[] filterInput(int[] unfiltered);

    public int process(int[] values) {
        int[] filtered = filterInput(values);
        // do something with filtered input
    }
}

class EvenValues extends Processor {
    protected int[] filterInput(int[] unfiltered) {
        // remove odd numbers
    }
}

class OddValues extends Processor {
    protected int[] filterInput(int[] unfiltered) {
        // remove even numbers
    }
}

1

ソリューション-基本クラス(抽象)

public abstract class Place {

String Name;
String Postcode;
String County;
String Area;

Place () {

        }

public static Place make(String Incoming) {
        if (Incoming.length() < 61) return (null);

        String Name = (Incoming.substring(4,26)).trim();
        String County = (Incoming.substring(27,48)).trim();
        String Postcode = (Incoming.substring(48,61)).trim();
        String Area = (Incoming.substring(61)).trim();

        Place created;
        if (Name.equalsIgnoreCase(Area)) {
                created = new Area(Area,County,Postcode);
        } else {
                created = new District(Name,County,Postcode,Area);
        }
        return (created);
        }

public String getName() {
        return (Name);
        }

public String getPostcode() {
        return (Postcode);
        }

public String getCounty() {
        return (County);
        }

public abstract String getArea();

}

1
すべてのコードをコードとしてフォーマットしてみて、説明を追加してください。現時点ではこれは答えとは言えません。
NomeN 2009

3
あなたがあなたのコードの説明をしなかったまで、そしてあなたがしなかった場合を除いて。あなたは悪い答えのメーカーと見なされます。だからここで説明をお願いします
devsda '16

0

抽象クラスは、抽象として宣言されたクラスであり、抽象メソッドを含む場合と含まない場合があります。抽象クラスはインスタンス化できませんが、サブクラス化できます。

つまり、抽象キーワードで宣言されたクラスは、Javaでは抽象クラスと呼ばれます。抽象メソッド(ボディのないメソッド)と非抽象メソッド(ボディのあるメソッド)を持つことができます。

重要な注意:- 抽象クラスを使用してオブジェクトをインスタンス化することはできません。実行時のポリモーフィズムに対するJavaのアプローチはスーパークラス参照を使用して実装されるため、オブジェクト参照を作成するために使用できます。したがって、サブクラスオブジェクトを指すために使用できるように、抽象クラスへの参照を作成できる必要があります。以下の例でこの機能を確認できます

abstract class Bike{  
  abstract void run();  
}  

class Honda4 extends Bike{  
    void run(){
        System.out.println("running safely..");
    }  

    public static void main(String args[]){  
       Bike obj = new Honda4();  
       obj.run();  
    }  
} 

0

抽象クラスは完全には実装されていませんが、サブクラスの青写真のようなものを提供します。完全に定義された具象メソッドを含むという点で部分的に実装される場合がありますが、抽象メソッドを保持することもできます。これらは、シグネチャはあるがメソッド本体はないメソッドです。サブクラスでは、抽象メソッドごとに本体を定義する必要があります。それ以外の場合も、抽象クラスとして宣言する必要があります。抽象クラスはインスタンス化できないため、使用するには少なくとも1つのサブクラスで拡張する必要があります。抽象クラスを汎用クラスと考えてください。欠落している情報を埋めるためにサブクラスがあります。


0

具象メソッドと非具象メソッドの両方を持つことができるクラス、つまり本文ありと本文なし。

  1. 実装のないメソッドには、「abstract」キーワードを含める必要があります。
  2. 抽象クラスはインスタンス化できません。

-1

それは何もせず、サブクラスのために共有される共通のテンプレートを提供するだけです

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.