0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

抽象クラスのメリット

Posted at

抽象クラスの使いどころと聞いて何を思い浮かべるでしょうか?
インターフェイスを使っていると、抽象クラスの存在は霞んでしまいがちです
しかし、明確なメリットもあります
それは...

  • 動的なフィールドを持てる事

インターフェースには無い明確な個性です
何故なら、抽象クラスに対しインターフェイスは静的なインスタンスであるため、動的フィールドを自身で運用できないためです
抽象クラスは、これを高度に隠ぺいします

コード一覧
Status.java
public abstract class Status{
        private int hp,atk;

        public Status(int hp,int atk){
                this.hp=hp;
                this.atk=atk;
        }

        protected void damage(Status target){
                int currentHP=0;
                target.hp=(currentHP=target.hp-this.atk)>=0?currentHP:0;
        }

        public void currentHP(){
                System.out.println(this+":"+this.hp);
        }
}
Fighter.java
public abstract class Fighter<T extends Status> extends Status{
        public Fighter(int hp,int atk){
                super(hp,atk);
        }

        public void attack(T target){
                super.damage(target);
        }
}
Player.java
public class Player extends Fighter<Enemy>{
        public Player(int hp,int atk){
                super(hp,atk);
        }
}
Enemy.java
public class Enemy extends Fighter<Player>{
        public Enemy(int hp,int atk){
                super(hp,atk);
        }
}
Main.java
public class Main{
        public static void main(String[] args){
                Player player=new Player(100,20);
                Enemy enemy=new Enemy(100,10);

                for(int i=10;i>0;--i)
                        player.attack(enemy);

                enemy.currentHP();
        }
}
結果
Enemy@27716f4:0

基本的なステータスを管理する抽象クラスとしてStatusを定義します
これはクラスなので、プライベートなフィールドを持つことが出来ます
受け取ったインスタンスがStatus型であれば自身のフィールドへアクセスできるので、これを操作するメソッドを定義できます
ここではdamageメソッドで操作します

Status.java
public abstract class Status{
        private int hp,atk;

        public Status(int hp,int atk){
                this.hp=hp;
                this.atk=atk;
        }

        protected void damage(Status target){
                int currentHP=0;
                target.hp=(currentHP=target.hp-this.atk)>=0?currentHP:0;
        }

        public void currentHP(){
                System.out.println(this+":"+this.hp);
        }
}

damageメソッドはprotectedなため、子クラスで呼び出すことが出来ます
しかしStatusと継承関係にないクラスからは呼び出せません
これによりインスタンスのパラメータに外部からはアクセスできないよう制限を設けることができます

Fighter.java
public abstract class Fighter<T extends Status> extends Status{
        public Fighter(int hp,int atk){
                super(hp,atk);
        }

        public void attack(T target){
                super.damage(target);
        }
}

attackメソッドには型引数Tが定義されています
子クラスとなる以下のPlayerEnemyでこの型引数にクラスを指定することで、引数の型を各インスタンスごとに変更することが出来ます

Player.java
public class Player extends Fighter<Enemy>{
        public Player(int hp,int atk){
                super(hp,atk);
        }
}
Enemy.java
public class Enemy extends Fighter<Player>{
        public Enemy(int hp,int atk){
                super(hp,atk);
        }
}

例えば現在Playerクラスから呼び出し可能なattackメソッドは、親クラスであるFighterの型引数にEnemyを指定しているため、Enemy型以外のインスタンスを受け付けません
そのためこのメソッドにPlayerのインスタンスを代入すると構文エラーを報告します

Main.java
public class Main{
	public static void main(String[] args){
		Player player=new Player(100,20);
		Enemy enemy=new Enemy(100,10);

		for(int i=10;i>0;--i)
			player.attack(player);

		enemy.currentHP();
	}
}
結果
Main.java:7: エラー: 不適合な型: PlayerをEnemyに変換できません:
                        player.attack(player);

このように、抽象クラスを用いることでパラメータがより厳格に保護されます

インスタンスが動的であることは、それ自体が一つの利点であると言えます
抽象クラスはそれ単体のインスタンス化を禁止したい場面でも役立ちます
今回のようにメソッドの適用対象を限定したいケースでは、あるクラスを継承することが確実に保証される手段として有用です

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?